Typescript SDK incorrectly transfers arguments to Sway smart contracts

I found a strange behavior of TypeScript SDK during working on a smart contract. It incorrectly passes arguments of type Vec<b256> to smart contract functions. Here is a simplified example:

Smart contract

The only point of this smart contract is to accept an argument of type Vec<b256> and log it to see exact values that were accepted by the contract.

contract;

storage {
    current_value: u8 = 0,
}

abi MyContract {
    #[storage(write)]
    fn test_function(value: u8, proof: Vec<b256>) -> u64;
}

impl MyContract for Contract {
    #[storage(write)]
    fn test_function(value: u8, proof: Vec<b256>) -> u64 {
        storage.current_value = value;
        let mut i = 0;
        while i < proof.len() {
            let elem: b256 = proof.get(i).unwrap();
            log(elem);
            i = i + 1;
        }
        0
    }
}
TypeScript code
async function demonstrateBug() {
    const provider = new Provider("https://beta-3.fuel.network/graphql")
    // test account
    const wallet = Wallet.fromPrivateKey("0x5b2ced0e90112c4b11af7266fc4e768c67e5efc1388830d3867cd4f636721e01", provider)
    const contractInstance = ContractAbi__factory.connect(
        CONTRACT_ID,
        wallet
    );

    const proof = [
        "0x4df54e3446d6beb23c7875c688498ca980bb51faef09c4b89ffb8fff0bbab01d",
        "0x8c1ee56d3419ab23abab2a80dc652d659ab5667b889f9e70cb9ed410f031502b",
    ]

    console.log("Expected proof: ", proof)

    const result =
        await contractInstance.functions.test_function(10, proof)
            .txParams({gasPrice: 1, gasLimit: 1000000})
            .call();

    console.log("Actual proof: ", result.logs)
}

When calling the typescript code, I get the following results:

Expected proof:  (2) ['0x4df54e3446d6beb23c7875c688498ca980bb51faef09c4b89ffb8fff0bbab01d', '0x8c1ee56d3419ab23abab2a80dc652d659ab5667b889f9e70cb9ed410f031502b']

Actual proof:  (2) ['0x8359cb0aeaa45b3a64ebaf4c2905919a58c2f022197729450000000000000000', '0x000000000000000000000000000000000000000000000000000000001dcd64ff']

It seems like there is a bug in the SDK when passing these arguments.

The same example works correctly with Rust SDK.

fuelup show
active toolchain
-----------------
beta-3-aarch64-apple-darwin (default)
  forc : 0.37.3
    - forc-client
      - forc-deploy : 0.37.3
      - forc-run : 0.37.3
    - forc-doc : 0.37.3
    - forc-explore : 0.28.1
    - forc-fmt : 0.37.3
    - forc-index : 0.11.2
    - forc-lsp : 0.37.3
    - forc-tx : 0.37.3
    - forc-wallet : 0.2.2
  fuel-core : 0.17.11
  fuel-indexer : 0.11.2

fuels versions
---------------
forc : 0.39

fuels-ts version: 0.38.0
fuels-rs version: 0.39.0

2 Likes

The same flow works correctly when I use arrays instead of vectors:

Smart contract with arrays
contract;

storage {
    current_value: u8 = 0,
}

abi MyContract {
    #[storage(write)]
    fn test_function(value: u8, proof: [b256; 2]) -> u64;
}

impl MyContract for Contract {
    #[storage(write)]
    fn test_function(value: u8, proof: [b256; 2]) -> u64 {
        storage.current_value = value;
        let mut i = 0;
        while i < 2 {
            let elem: b256 = proof[i];
            log(elem);
            i = i + 1;
        }
        0
    }
}

Hi, can you please try using the latest TS SDK version (0.45.0) and see if the problem persists?

Hi, I changed both the typegen and the ts sdk versions to 0.45.0 and now I’m getting the following error:

Uncaught (in promise) Error: Unknown field "txCreatedIdx" on type "Coin".: 
{
  "response": {
    "data": null,
    "errors": [
      {
        "message": "Unknown field \"txCreatedIdx\" on type \"Coin\".",
        "locations": [
          {
            "line": 20,
            "column": 3
          }
        ]
      },
      {
        "message": "Unknown type \"MessageCoin\"",
        "locations": [
          {
            "line": 23,
            "column": 1
          }
        ]
      },
      {
        "message": "Unknown field \"coinsToSpend\" on type \"Query\". Did you mean \"resourcesToSpend\"?",
        "locations": [
          {
            "line": 2,
            "column": 3
          }
        ]
      }
    ],
    "status": 200,
    "headers": {
      "map": {
        "content-type": "application/json"
      }
    }
  },
  "request": {
    "query": "query getCoinsToSpend($owner: Address!, $queryPerAsset: [SpendQueryElementInput!]!, $excludedIds: ExcludeInput) {\n  coinsToSpend(\n    owner: $owner\n    queryPerAsset: $queryPerAsset\n    excludedIds: $excludedIds\n  ) {\n    ...coinFragment\n    ...messageCoinFragment\n  }\n}\n\nfragment coinFragment on Coin {\n  __typename\n  utxoId\n  owner\n  amount\n  assetId\n  maturity\n  blockCreated\n  txCreatedIdx\n}\n\nfragment messageCoinFragment on MessageCoin {\n  __typename\n  sender\n  recipient\n  nonce\n  amount\n  assetId\n  daHeight\n}",
    "variables": {
      "owner": "0x984ac4a0e975137f905b02c0bca41bdb6020070a898e0418c7d5bc5b7562bc02",
      "queryPerAsset": [
        {
          "assetId": "0x0000000000000000000000000000000000000000000000000000000000000000",
          "amount": "10"
        }
      ],
      "excludedIds": {
        "messages": [],
        "utxos": []
      }
    }
  }
}

Hi, that makes sense since 0.45 is not compatible with beta-3. My bad. Can you try using 0.44 instead?

Seems like it’s working with version 0.44, thanks. Is it safe to use 0.44 with beta-3? The recommended version in the fuel book is 0.38.0 (source)

Yeah, you should be fine using 0.44 too. Please let us know in case something comes up, though!

1 Like

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.