Skip to content

Use token bound accounts (ERC6551)

In this guide you'll find how to implement a token bound account based on a game character. This guide implements the backend code necessary to use token bound accounts in your game. It uses the Openfort Node SDK. Architecture of the solution proposed in this guide:

using-token-bound-accounts

Prerequisites

When interacting with smart wallets through Openfort, import the contract and create a policy to sponsor gas. Get your secret key from the Openfort dashboard.

1. Import the NFT contract

Token bound accounts are based on the ownership of a non-fungible token. In this guide, we'll use a contract, deployed at in Polygon Amoy at 0x380...AC0.

Dashboard

Add a new contract by clicking the Add contract button in the Asset contracts section, then enter:

  • The name of the contract (it can be any name you want; the name is only for identification purposes)
  • The network (chainId) where the smart contract is located: 80002.
  • The address of the contract.
  • Because the contract is verified in the block explorer, there's no need to provide an ABI manually.
DashboardAddContract

API Methods

curl
curl https://api.openfort.io/v1/contracts \
    -H "Authorization: Bearer $YOUR_SECRET_KEY" \
    -d name="SimpleNFT" \
    -d address="0xbed6a05ce8719bc00db1cc81a814192c82be1bb1" \
    -d chainId="80002"
Node.js
const openfort = new Openfort(process.env.YOUR_SECRET_KEY);
const SimpleNFT = await openfort.contracts.create({
    name: "SimpleNFT",
    address: "0xbed6a05ce8719bc00db1cc81a814192c82be1bb1",
    chainId: chainId,
});

2. Set up gas sponsoring

Dashboard

Add a new policy by clicking the Add policy button in the Policies page, then enter:

  • The name of the policy (it can be any name you want; the name is only for identification purposes)
  • The network (chainId) where the smart contract is located: 80002.
  • As a fee sponsorship strategy, leave pay gas for user.
  • Under policy rules, create two rules:
    • 1st rule:
      • Leave contract_functions under Rule model.
      • Under Contract, select the contract you imported in the previous step.
      • Under Function name, select All functions.
    • 2nd rule:
      • Choose account_functions under Rule model.
GasPolicy

API Methods

curl
curl https://api.openfort.io/v1/policies \
    -H "Authorization: Bearer $YOUR_SECRET_KEY" \
    -d name="Trial sponsor" \
    -d chainId="80002" \
    -d "strategy[sponsorSchema]=pay_for_user"

Use the policy id to create policy rules and use the previously imported contract.

curl https://api.openfort.io/v1/policies/:id/policy_rules \
    -H "Authorization: Bearer $YOUR_SECRET_KEY" \
    -d type="contract_functions" \
    -d functionName="All functions" \
    -d contract="con_..."
curl https://api.openfort.io/v1/policies/:id/policy_rules \
    -H "Authorization: Bearer $YOUR_SECRET_KEY" \
    -d type="account_functions"
Node.js
const policy = await openfort.policies.create({
    name: "test",
    chainId: 80002,
    strategy: {
        sponsorSchema: SponsorSchema.PayForUser,
    },
});
 
await openfort.policyRules.create({
    contract: SimpleNFT.id,
    type: PolicySchema.ContractFunctions,
    policy: policy.id,
    functionName: "All functions",
});
 
await openfort.policyRules.create({
    type: PolicySchema.AccountFunctions,
    policy: policy.id,
});

Create the character NFT

As you know, to create a token bound account, you need to first know what NFT will be used to define the ownership of the account. Because in this guide we start from scratch, we'll also mint that NFT, but you could potentially use any NFT that you already own. If you want to use your own NFT, skip to the next section.

We'll use a regular Openfort account to mint it. We start by creating an Openfort player and a regular Openfort account.

const upgradeable_account = await openfort.accounts.create({
  chainId: chainId,
});
const player_upgradeable = upgradeable_account.player.id;

Then, we define the mint function interaction and create a transactionIntent using the gas sponsoring policy we created in the previous step.

const interactionsMintOwnerNFT: Interaction[] = [
  {
    contract: SimpleNFT.id,
    functionName: 'mint',
    functionArgs: [player_upgradeable.id],
  },
]
const transactionIntentSimpleNFT = await openfort.transactionIntents.create({
  player: player_upgradeable.id,
  interactions: interactionsMintOwnerNFT,
  policy: policy.id,
  chainId: chainId,
  optimistic: false,
})

And we're all set! The character NFT is minted and owned by the player_upgradeable's account.

Create a token bound account

Now that we have the NFT that will defined the ownership of the account, we can create a token bound account. Note that when creating the token bound account, we need to specify:

  • The externalOwnerAddress field: the address that currently owns the character NFT.
  • The tokenContract field: the address of the NFT contract.
  • The tokenId field: the token id of the character NFT.
  • The accountType field: specified that the account to be created is a token bound account.

We start off by creating a new player and then assign a new counterfactual token bound account to it:

const player_6551 = await openfort.players.create({
  name: '6551 account',
})
const account = await openfort.accounts.create({
  player: player_6551.id,
  chainId: chainId,
  accountType: DataAccountTypes.Erc6551,
  tokenContract: SimpleNFT.id,
  tokenId: simpleOwnerNFTTokenId,
  externalOwnerAddress: upgradeable_account.address,
})

Transferring the character NFT

You can send the character NFT to another account by calling the safeTransferFrom function of the NFT contract. This function takes the address of the current owner, the address of the new owner, and the token id as arguments.

In this case, we'll transfer the character NFT to the previously account ownerAddress. This is because Openfort accounts come, by default, with a managed signer. Sending the NFT to the account ownerAddress allows a regular EOA to control the account.

const interactionsExecCall: Interaction[] = [
  {
    contract: SimpleNFT.id,
    functionName: 'approve',
    functionArgs: [ownerAddress, simpleOwnerNFTTokenId],
  },
  {
    contract: SimpleNFT.id,
    functionName: 'safeTransferFrom',
    functionArgs: [
      player_upgradeable.id,
      account.ownerAddress,
      simpleOwnerNFTTokenId,
    ],
  },
]
const transactionIntentTransferOwnerNFT =
  await openfort.transactionIntents.create({
    player: player_upgradeable.id,
    interactions: interactionsExecCall,
    policy: policy.id,
    chainId: chainId,
    optimistic: false,
  })

Minting an asset into the character NFT

You can easily mint assets into the token bound account and even use it to mint assets. Here we interact with the same simple NFT contract to mint an NFT into the token bound account.

const interactionsMintNFT: Interaction[] = [
  {
    contract: SimpleNFT.id,
    functionName: 'mint',
    functionArgs: [player_6551.id],
  },
]
const transactionIntentSimpleNFT6551 = await openfort.transactionIntents.create(
  {
    player: player_6551.id,
    interactions: interactionsMintNFT,
    policy: policy.id,
    chainId: chainId,
    optimistic: false,
  }
)