Skip to content

Write to Blockchain

The blockchain can be thought of as a general-purpose, publically viewable and verified, database. To write to a blockchain, similar to with a typical database, you must make a transaction.

Typically, creating a blockchain transaction is rather complex, but WaaS handles that complexity for you and exposes 5 types of Transactions.

Sending a transaction is an asynchronous Task. You can use await when calling WaaSWallet.SendTransaction from within an async Task if you wish to obtain the TransactionReturn object directly. Or, you can take the recommended approach which is to setup handler functions for the WaaSWallet.OnSendTransactionComplete and WaaSWallet.OnSendTransactionFailed events and call the WaaSWallet.SendTransaction method from anywhere (without await). For example:

public void OnSendTransactionCompleteHandler(SuccessfulTransactionReturn result) {
    // Do something
}

public void OnSendTransactionFailedHandler(FailedTransactionReturn result) {
    // Do something
}

public void OnWaaSWalletCreatedHander(WaaSWallet wallet) {
    wallet.OnSendTransactionComplete += OnSendTransactionCompleteHandler;
    wallet.OnSendTransactionFailed += OnSendTransactionFailedHandler;
}

If you're unfamiliar with working with events in Unity, check out this great Reddit post!

RawTransaction

The most basic form of a Transaction, a raw transaction is very useful to send ETH or the gas currency of the network you are interacting with to an Address.

For example, to send one MATIC to 0x9766bf76b2E3e7BCB8c61410A3fC873f1e89b43f you can use this snippet:

_wallet.SendTransaction(
    Chain.Polygon,
    new SequenceSDK.WaaS.Transaction[]
    {
        new RawTransaction("0x9766bf76b2E3e7BCB8c61410A3fC873f1e89b43f", DecimalNormalizer.Normalize(1))
    });

where _wallet is a WaaSWallet.

Note: the EVM does not support floating point numbers. As a result, token (and gas currency) values are represented by whole numbers and a "decimals" value. 1 ETH (or in the example above 1 MATIC) is represented as 1000000000000000000 (1 * 10^18) as ETH, MATIC, and most gas currencies have a "decimals" value of 18. DecimalNormalizer.Normalize (above) is a basic helper function that will return input value * 10^decimals and optionally accepts a "decimals" value as a second parameter (defaulting to 18 when not provided).

Additionally, you can include data with a raw transaction in hexadecimal format as a string. For more on this, please see the advanced section of this documentation.

sendERC20

An ERC20 token is the fungible token standard. You can easily deploy an ERC20 contract and mint tokens using our Builder. Learn how in our Builder documentation.

To send an ERC20 token transaction, you can use this code snippet:

_wallet.SendTransaction(Chain.Polygon, new SequenceSDK.WaaS.Transaction[]
    {
        new SendERC20(
            erc20TokenAddress,
            ToAddress,
            AmountAsString),
    });

Note: as above, it is recommended to use DecimalNormalizer.Normalize to convert the amount from human readable format to EVM format. Please make sure to include the optional "decimals" int parameter if your ERC20 token has a "decimals" value that is not 18. If you're not sure how many "decimals" your ERC20 has, this can be easily read on the Builder using the "decimals" method under "Read Contract".

Complex ERC20 Interactions

For interactions with ERC20 tokens outside of basic transfers, you'll want to use our SequenceEthereum library provided with the SDK. We've created ERC20 smart contract wrapper functions for your convenience that allow you to create and send RawTransactions with WaaS.

First, you'll need to create an ERC20 object by providing a contract address and optionally, an ABI string, if you are using a custom variation of the ERC20 standard (not recommended).

ERC20 myToken = new ERC20(myTokenAddress);

with this reference, you'll have access to all of the methods implemented by the ERC20 class. Any method that returns a CallContractFunction, e.g. Mint, can be used when creating a RawTransaction with WaaS. For example:

ERC20 myToken = new ERC20(myTokenAddress);
_wallet.SendTransaction(Chain.Polygon, new SequenceSDK.WaaS.Transaction[]
    {
        new RawTransaction(myToken.Mint(toAddress, DecimalNormalizer.NormalizeAsBigInteger(amount))),
    });

sendERC721

An ERC721 token is the non-fungible standard, you've probably heard of them as NFTs. You can easily deploy an ERC721 contract and mint tokens using our Builder. Learn how in our Builder documentation.

To send an ERC721 token transaction, you can use this code snippet:

_wallet.SendTransaction(Chain.Polygon, new SequenceSDK.WaaS.Transaction[]
    {
        new SendERC721(
            erc721TokenAddress,
            ToAddress,
            TokenIdAsString),
    });

Complex ERC721 Interactions

For interactions with ERC721 tokens outside of basic transfers, you'll want to use our SequenceEthereum library provided with the SDK. We've created ERC21 smart contract wrapper functions for your convenience that allow you to create and send RawTransactions with WaaS.

First, you'll need to create an ERC721 object by providing a contract address and optionally, an ABI string, if you are using a custom variation of the ERC721 standard (not recommended).

ERC721 myToken = new ERC721(myTokenAddress);

with this reference, you'll have access to all of the methods implemented by the ERC721 class. Any method that returns a CallContractFunction, e.g. SafeMint, can be used when creating a RawTransaction with WaaS. For example:

ERC721 myToken = new ERC721(myTokenAddress);
_wallet.SendTransaction(Chain.Polygon, new SequenceSDK.WaaS.Transaction[]
    {
        new RawTransaction(myToken.SafeMint(toAddress)),
    });

sendERC1155

An ERC1155 token is the multi token standard, often referred to as SFTs (semi-fungible tokens). As co-creators of the ERC1155 standard we are firm believers in its unparalleled usefulness for games. You can easily deploy an ERC1155 contract and mint tokens using our Builder. Learn how in our Builder documentation.

To send an ERC1155 token transaction, you can use this code snippet:

_wallet.SendTransaction(Chain.Polygon, new SequenceSDK.WaaS.Transaction[]
    {
        new SendERC1155(
            erc1155TokenAddress,
            ToAddress,
            new SendERC1155Values[]
            {
                new SendERC1155Values(TokenIdAsString, AmountAsString),
                ...
            }),
    });

Note: you can send multiple token ids from the same ERC1155 contract in a single transaction by including multiple SendERC1155Values objects in the transaction

Complex ERC1155 Interactions

For interactions with ERC1155 tokens outside of basic transfers, you'll want to use our SequenceEthereum library provided with the SDK. We've created ERC1155 smart contract wrapper functions for your convenience that allow you to create and send RawTransactions with WaaS.

First, you'll need to create an ERC1155 object by providing a contract address and optionally, an ABI string, if you are using a custom variation of the ERC1155 standard (not recommended).

ERC1155 myToken = new ERC1155(myTokenAddress);

with this reference, you'll have access to all of the methods implemented by the ERC1155 class. Any method that returns a CallContractFunction, e.g. Mint, can be used when creating a RawTransaction with WaaS. For example:

ERC1155 myToken = new ERC1155(myTokenAddress);
_wallet.SendTransaction(Chain.Polygon, new SequenceSDK.WaaS.Transaction[]
    {
        new RawTransaction(myToken.Mint(toAddress, tokenId, amount)),
    });

DelayedEncode

When calling a smart contract on an EVM-based network, the client goes through a complex process known as "ABI encoding" where the function signature you want to call as well as the parameters you're providing are encoded into a binary format. This process is complicated and error-prone so we've abstracted it all away so that you don't have to deal with it. But, if you're curious to learn how it works, please see this document.

A DelayedEncode transaction allows you to call any method on an arbitrary smart contract, allowing us to handle the complicated ABI encoding process.

To send a DelayedEncode transaction, you can use this code snippet:

_wallet.SendTransaction(Chain.Polygon, new SequenceSDK.WaaS.Transaction[]
    {
        new DelayedEncode(ContractAddress, ValueAsString, new DelayedEncodeData(
            ContractABIAsString,
            ParametersAsObjectArray,
            FunctionNameAsString)),
    });

Let's examine the above to get a better understanding of some of the variables that may be non-obvious.

ValueAsString: This will usually be "0" unless you are calling a payable method denoted by the payable keyword in the smart contract definition. If you are calling a payable method, it is recommended to use DecimalNormalizer.Normalize to convert the amount from human readable format to EVM format.

ContractABIAsString: This can either be the entire ABI or just the function you plan on interacting with. If you're not familiar with ABIs, we'd recommend copy-pasting the function signature (with parameters) from the contract source code on Etherscan (or the appropriate block explorer for your network) and removing the whitespace and variable names.

ParametersAsObjectArray: The parameters you want to provide to the method you wish to call. No need to provide the parameter names, just their values in the order they appear in the ABI. Provide parameters in string format when in doubt.

FunctionNameAsString: The name of the function you want to call as it appears in the ABI (or source code). Exclude parentheses and parameters.

Putting this together, an example of using delayed encode to call the "mint" function on an ERC20 would look like this:

_wallet.SendTransaction(Chain.Polygon, new SequenceSDK.WaaS.Transaction[]
    {
        new DelayedEncode(ContractAddress, "0", new DelayedEncodeData(
            "mint(address,uint256)",
            new object[]
            {
                ToAddress, DecimalNormalizer.Normalize(1)
            },
            "mint")),
    });

Batch Transactions

Using the magic of the Sequence Smart Contract wallet, our SDK allows you to seemlessly batch transactions together. Batching transactions together is extremely beneficial as it provides material gas savings and allows you to create complex transactions, that either all pass or all fail, without deploying custom smart contracts for each bespoke use case, opening a whole new realm of design possibilities!

Sending a batch transaction is easy! Simply include multiple transactions, of any type, in your transaction array when making the SendTransaction request.

For example - sending a transaction of each type in a batch:

_wallet.SendTransaction(
    Chain.Polygon,
    new SequenceSDK.WaaS.Transaction[]
    {
        new RawTransaction(ToAddress, DecimalNormalizer.Normalize(1)),
        new SendERC20(
            erc20TokenAddress,
            ToAddress,
            AmountAsString),
        new RawTransaction(new ERC20(erc20TokenAddress).Burn(DecimalNormalizer.NormalizeAsBigInteger(amount))),
        new SendERC721(
            erc721TokenAddress,
            ToAddress,
            TokenIdAsString),
        new SendERC1155(
            erc1155TokenAddress,
            ToAddress,
            new SendERC1155Values[]
            {
                new SendERC1155Values(TokenIdAsString, AmountAsString),
                ...
            }),
        new DelayedEncode(ContractAddress, ValueAsString, new DelayedEncodeData(
            ContractABIAsString,
            ParametersAsObjectArray,
            FunctionNameAsString)),
    });

Since these transactions are all batched into a single transaction by the Sequence Smart Contract Wallet before being submitted to the network, you will receive only one transaction receipt.

FeeOptions

By default, the SDK will automatically sponsor all Embedded Wallet/WaaS Wallet transactions using your Builder API credits. However, in some niche use cases, you may find that you would prefer not to sponsor your users' transactions. This requires that your users are more experienced Web3 users and have tokens/gas currency in their wallet that can be used to pay gas fees. In addition to the gas currency for the selected network, gas fees can also be paid using select ERC20 and ERC1155 tokens.

First, you'll need to assemble the transaction(s) you wish to submit in a batch. Then, you need to request the FeeOptions.

Transaction[] transactions = new Transaction[]
{
    // Create your transactions here
};
FeeOptionsResponse response = await _wallet.GetFeeOptions(chain, transactions);

The FeeOptionsResponse contains a FeeQuote (string) that locks in the price for each FeeOptionReturn in the FeeOptions array that is returned for a limited time; you'll need this in a moment when submitting your transactions. For your convenience, the SDK will automatically query the user's wallet to see which of the FeeOptions the user can afford using the Indexer.

From here, you can display a UI to the user to allow them to select how they would like to pay the fee for their transactions.

Once the user has selected how they'd like to pay their fee, you may submit the transactions, including the selected FeeOption and the FeeQuote string.

_wallet.SendTransactionWithFeeOptions(chain, transactions, response.FeeOptions[selectionIndex].FeeOption, response.FeeQuote);

In the Demo Scene that can be imported via Package Manager > Samples, you can see a barebones example usage of FeeOptions. Here, we do not provide a UI and instead opt to use the first available FeeOption in the user's wallet. We do not recommend using this approach in a real game, but it serves as a useful example for your own integration. See our sample code below:

private async Task WaitForFeeOptionsAndSubmitFirstAvailable(Address toAddress, string amount)
{
    Transaction[] transactions = new Transaction[]
    {
        new RawTransaction(toAddress, amount)
    };
    FeeOptionsResponse response = await _wallet.GetFeeOptions(_chain, transactions)
    int options = response.FeeOptions.Length;
    for (int i = 0; i < options; i++)
    {
        if (response.FeeOptions[i].InWallet)
        {
            await _wallet.SendTransactionWithFeeOptions(_chain, transactions, response.FeeOptions[i].FeeOption,
                response.FeeQuote);
            return;
        }
    }
    
    Debug.LogError("The user does not have enough of the valid FeeOptions in their wallet");
}