`transfer` doesn't work as expected with `forc` `0.48.1` and `fuels` `0.53.0`
Hey all,
Been playing with the nightly toolchain of fuelup for some projects we're building @LearnWeb3DAO because of a bug fix that was patched in that version and exists in beta-4.
I've noticed the transfer function from std::token::transfer doesn't work nicely for some reason.
Here is the relevant code along with a following explanation:
#[storage(read, write)]
fn claim(stream_id: u64) {
let sender = msg_sender().unwrap();
let mut stream = storage.streams.get(stream_id).read();
if stream.recipient != sender {
revert(UNAUTHORIZED);
}
let current_block = height();
let block_delta = stream.get_blocks_delta(current_block);
let total_claimable_until_now = stream.amount_per_block * block_delta.as_u64();
let claimable_now = total_claimable_until_now - stream.claimed;
stream.claimed = total_claimable_until_now;
storage.streams.insert(stream_id, stream);
// Relevant parts
log(stream.recipient);
log(stream.asset_id);
log(claimable_now);
transfer(stream.recipient, stream.asset_id, claimable_now);
}
transfer in the above code is std::token::transfer
In my test harness, I'm doing:
contract_instance
.with_account(recipient_wallet.clone())
.unwrap()
.methods()
.claim(stream_id)
.call()
.await
.expect("Failed to claim");
where stream_id is a valid u64.
This results in a panic:
Failed to claim: RevertTransactionError { reason: "failed transfer to address.", revert_id: 18446744073709486081, receipts: [Call { id: 0000000000000000000000000000000000000000000000000000000000000000, to: 67433f736acb6a5ee7985f34a2b0908231678c08791deb3e08b09258128f0924, amount: 0, asset_id: 0000000000000000000000000000000000000000000000000000000000000000, gas: 2571, param1: 2892928773, param2: 0, pc: 11632, is: 11632 }, LogData { id: 67433f736acb6a5ee7985f34a2b0908231678c08791deb3e08b09258128f0924, ra: 0, rb: 0, ptr: 33208, len: 40, digest: 6905085070841ddc8a266f5c2ae6b0cfb8a62a241f1befeaaac9df3340c3f094, pc: 14168, is: 11632, data: Some(000000000000000009c0b2d1a4...) }, LogData { id: 67433f736acb6a5ee7985f34a2b0908231678c08791deb3e08b09258128f0924, ra: 0, rb: 1, ptr: 33248, len: 32, digest: 66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925, pc: 14196, is: 11632, data: Some(00000000000000000000000000...) }, Log { id: 67433f736acb6a5ee7985f34a2b0908231678c08791deb3e08b09258128f0924, ra: 30, rb: 2, rc: 0, rd: 0, pc: 14204, is: 11632 }, Revert { id: 67433f736acb6a5ee7985f34a2b0908231678c08791deb3e08b09258128f0924, ra: 18446744073709486081, pc: 14428, is: 11632 }, ScriptResult { result: Revert, gas_used: 2679 }] }
Or, a prettified JSON version:
{
"reason": "failed transfer to address.",
"revert_id": "18446744073709486081",
"receipts": [
{
"id": "0000000000000000000000000000000000000000000000000000000000000000",
"to": "67433f736acb6a5ee7985f34a2b0908231678c08791deb3e08b09258128f0924",
"amount": 0,
"asset_id": "0000000000000000000000000000000000000000000000000000000000000000",
"gas": 2571,
"param1": 2892928773,
"param2": 0,
"pc": 11632,
"is": 11632
},
{
"id": "67433f736acb6a5ee7985f34a2b0908231678c08791deb3e08b09258128f0924",
"ra": 0,
"rb": 0,
"ptr": 33208,
"len": 40,
"digest": "6905085070841ddc8a266f5c2ae6b0cfb8a62a241f1befeaaac9df3340c3f094",
"pc": 14168,
"is": 11632,
"data": "Some(000000000000000009c0b2d1a4...)"
},
{
"id": "67433f736acb6a5ee7985f34a2b0908231678c08791deb3e08b09258128f0924",
"ra": 0,
"rb": 1,
"ptr": 33248,
"len": 32,
"digest": "66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925",
"pc": 14196,
"is": 11632,
"data": "Some(00000000000000000000000000...)"
},
{
"id": "67433f736acb6a5ee7985f34a2b0908231678c08791deb3e08b09258128f0924",
"ra": 30,
"rb": 2,
"rc": 0,
"rd": 0,
"pc": 14204,
"is": 11632
},
{
"id": "67433f736acb6a5ee7985f34a2b0908231678c08791deb3e08b09258128f0924",
"ra": 18446744073709486000,
"pc": 14428,
"is": 11632
},
{
"result": "Revert",
"gas_used": 2679
}
]
}
As you can see in the first log receipt for Call, it says amount: 0
The transfer docs are clear that it will fail if amount = 0 - this is fine
The weird part is, claimable_now (the amount) isn't actually zero.
Looking at the later logs where I logged the 3 variable inputs to transfer, we can see in the last one ra: 30 which represents the value of claimable_now RIGHT BEFORE the transfer call
Something somewhere is going wrong and causing the transfer call to think I'm passing in an amount of zero, when I'm actually passing it an amount of 30.
Could you add .append_variable_outputs(1) to your sdk harness call so that it looks like this and try again?
contract_instance
.with_account(recipient_wallet.clone())
.unwrap()
.methods()
.claim(stream_id)
.append_variable_outputs(1)
.call()
.await
.expect("Failed to claim");
Could you add
.append_variable_outputs(1)to your sdk harness call so that it looks like this and try again?contract_instance .with_account(recipient_wallet.clone()) .unwrap() .methods() .claim(stream_id) .append_variable_outputs(1) .call() .await .expect("Failed to claim");
This fixed it - can you explain what happened here? How can the docs be improved to make this clear?
Could you add
.append_variable_outputs(1)to your sdk harness call so that it looks like this and try again?contract_instance .with_account(recipient_wallet.clone()) .unwrap() .methods() .claim(stream_id) .append_variable_outputs(1) .call() .await .expect("Failed to claim");This fixed it - can you explain what happened here? How can the docs be improved to make this clear?
This is part of the Rust SDK. Documentation on this can be found here: https://rust.fuel.network/v0.55.0/calling-contracts/variable-outputs.html