Skip to content

FuturePass (EVM example) ​

Create a FuturePass, make a call through it and register a delegate. Reference: FuturePass precompile.

Copied from Futureverse’s futureversecom/trn-examples, deleted with its GitHub organisation, at commit 44c9e16 of 2024-10-31. Apache-2.0: license. Before you run it, read Running the examples.

You don’t need a FuturePass

This page treats FuturePass as something to use. You don’t need one: where the page uses a FuturePass, use an account in your own wallet instead — any EIP-6963 wallet app, MetaMask preferred. If you have a FuturePass, you can move what’s in it to your wallet at any time: https://futurepass.trn.codeology.co.nz/wallet/futurepass. FuturePass explains how it works.

Offline destinations

Some places this page points to did not answer when last checked:

  • docs-beta.therootnetwork.com — does not resolve (checked 2026-10-18)
  • stackblitz.com/github/futureversecom — StackBlitz loads these projects from the deleted futureversecom organisation on GitHub (checked 2026-10-18)

Run in StackBlitz (repository deleted, checked 2026-10-18) · Precompile Documentation (offline, checked 2026-10-18)

IMPORTANT

Ensure the following ENV vars are available before running the examples

Examples ​

bash
# change your working directory to this example first
cd examples/substrate/use-futurepass

# export all required environments
export CALLER_PRIVATE_KEY=

# creates new FPass account
pnpm call:createAccount

# call a call as FPass account
pnpm call:proxyCall

# register a delegate to FPass account
pnpm call:registerDelegate

Source files ​

src/createAccount.ts ​

ts
import { FUTUREPASS_REGISTRAR_PRECOMPILE_ABI } from "@therootnetwork/evm";
import { filterTransactionEvents } from "@trne/utils/filterTransactionEvents";
import { getFuturepassRegistrarContract } from "@trne/utils/getFuturepassRegistrarContract";
import { withEthersProvider } from "@trne/utils/withEthersProvider";
import { ContractReceipt, Wallet } from "ethers";

/**
 * Use `registrar.create` call to create a FPass account.
 *
 * Assumes the caller has XRP to pay for gas.
 */
withEthersProvider("porcini", async (provider, wallet, logger) => {
	const owner = Wallet.createRandom();
	const registrar = getFuturepassRegistrarContract().connect(wallet);

	logger.info(
		{
			parameters: {
				contractAddress: registrar.address,
				owner: owner.address,
			},
		},
		`create "registrar.create" call`
	);

	logger.info(`dispatch transaction from wallet=${wallet.address}`);
	const tx = await registrar.create(owner.address);

	const receipt = (await tx.wait()) as unknown as ContractReceipt;

	const [createEvent] = filterTransactionEvents(FUTUREPASS_REGISTRAR_PRECOMPILE_ABI, receipt.logs, [
		"FuturepassCreated",
	]);

	logger.info(
		{
			result: {
				transactionHash: receipt.transactionHash,
				blockNumber: receipt.blockNumber,
				createEvent: {
					name: createEvent.name,
					args: createEvent.args,
				},
			},
		},
		"receive result"
	);
});

src/proxyCall.ts ​

ts
import { ERC20_ABI, FUTUREPASS_PRECOMPILE_ABI } from "@therootnetwork/evm";
import { ALICE } from "@trne/utils/accounts";
import { filterTransactionEvents } from "@trne/utils/filterTransactionEvents";
import { getERC20Contract } from "@trne/utils/getERC20Contract";
import { getFuturepassContract } from "@trne/utils/getFuturepassContract";
import { getFuturepassRegistrarContract } from "@trne/utils/getFuturepassRegistrarContract";
import { ASTO_ASSET_ID } from "@trne/utils/porcini-assets";
import { withEthersProvider } from "@trne/utils/withEthersProvider";
import { ContractReceipt, utils } from "ethers";
import assert from "node:assert";

const CALL_TYPE = {
	StaticCall: 0,
	Call: 1,
	DelegateCall: 2,
	Create: 3,
	Create2: 4,
};

/**
 * Use `futurepass.proxyCall` call to call `erc20.transfer` and have the FPass account transfers
 * the amount
 *
 * Assume wallet has XRP to pay for gas.
 */
withEthersProvider("porcini", async (provider, wallet, logger) => {
	const registrar = getFuturepassRegistrarContract().connect(provider);
	const fpAccount = await registrar.futurepassOf(wallet.address);
	assert(fpAccount);
	logger.info(
		{
			futurepass: {
				holder: wallet.address,
				account: fpAccount.toString(),
			},
		},
		"futurepass details"
	);
	const fp = getFuturepassContract(fpAccount).connect(wallet);

	const transferAmount = utils.parseUnits("0.1", 18); // 0.1 ASTO
	const transferToken = getERC20Contract(ASTO_ASSET_ID);
	const transferCalldata = new utils.Interface(ERC20_ABI).encodeFunctionData("transfer", [
		ALICE,
		transferAmount,
	]);

	logger.info(
		{
			parameters: {
				callType: CALL_TYPE.Call,
				callTarget: transferToken.address,
				value: "0",
				callData: transferCalldata,
			},
		},
		`create a "futurepass.proxyCall" call`
	);

	logger.info(`dispatch transaction from wallet=${wallet.address}`);
	const tx = await fp.proxyCall(CALL_TYPE.Call, transferToken.address, "0", transferCalldata);

	const receipt = (await tx.wait()) as unknown as ContractReceipt;
	const [transferEvent, executedEvent] = filterTransactionEvents(
		[...ERC20_ABI, ...FUTUREPASS_PRECOMPILE_ABI],
		receipt.logs,
		["Transfer", "Executed"]
	);

	logger.info(
		{
			result: {
				transactionHash: receipt.transactionHash,
				blockNumber: receipt.blockNumber,
				transferEvent: {
					name: transferEvent.name,
					args: transferEvent.args,
				},

				executedEvent: {
					name: executedEvent.name,
					args: executedEvent.args,
				},
			},
		},
		"receive result"
	);
});

src/registerDelegate.ts ​

ts
import { FUTUREPASS_PRECOMPILE_ABI } from "@therootnetwork/evm";
import { filterTransactionEvents } from "@trne/utils/filterTransactionEvents";
import { getFuturepassContract } from "@trne/utils/getFuturepassContract";
import { getFuturepassRegistrarContract } from "@trne/utils/getFuturepassRegistrarContract";
import { withEthersProvider } from "@trne/utils/withEthersProvider";
import { ContractReceipt, utils as ethers, Wallet } from "ethers";
import assert from "node:assert";

enum ProxyType {
	NoPermission = 0,
	Any = 1,
}

/**
 * Use `futurepass.registerDelegateWithSignature` call to add delegate to an existing FPass account
 *
 * Assumes wallet has XRP to pay for gas.
 */
withEthersProvider("porcini", async (provider, wallet, logger) => {
	const delegate = Wallet.createRandom();
	const proxyType = ProxyType.Any;
	// recommended low number, 75 blocks ~= 5 minutes
	const deadline = (await provider.getBlockNumber()) + 75;
	const registrar = getFuturepassRegistrarContract().connect(provider);
	const fpAccount = await registrar.futurepassOf(wallet.address);
	assert(fpAccount);
	logger.info(
		{
			futurepass: {
				holder: wallet.address,
				account: fpAccount.toString(),
			},
		},
		"futurepass details"
	);

	const message = ethers
		.solidityKeccak256(
			["address", "address", "uint8", "uint32"],
			[fpAccount, delegate.address, proxyType, deadline]
		)
		.substring(2);
	logger.info(
		{
			payload: {
				fpAccount,
				address: delegate.address,
				proxyType,
				deadline,
			},
		},
		"require delegate to sign a payload"
	);
	const signature = await delegate.signMessage(message);

	logger.info(
		{
			parameters: {
				fpAccount,
				address: delegate.address,
				proxyType,
				deadline,
				signature,
			},
		},
		`create a "futurepass.registerDelegateWithSignature" call`
	);

	const fp = getFuturepassContract(fpAccount).connect(wallet);
	logger.info(`dispatch transaction from wallet=${wallet.address}`);
	const tx = await fp.registerDelegateWithSignature(
		delegate.address,
		proxyType,
		deadline,
		signature
	);

	const receipt = (await tx.wait()) as unknown as ContractReceipt;

	const [delegatedEvent] = filterTransactionEvents(FUTUREPASS_PRECOMPILE_ABI, receipt.logs, [
		"FuturepassDelegateRegistered",
	]);

	logger.info(
		{
			result: {
				transactionHash: receipt.transactionHash,
				blockNumber: receipt.blockNumber,
				delegatedEvent: {
					name: delegatedEvent.name,
					args: delegatedEvent.args,
				},
			},
		},
		"receive result"
	);
});

package.json ​

json
{
  "scripts": {
    "call": "tsx --tsconfig ../../../tsconfig.json",
    "call:createAccount": "pnpm call src/createAccount.ts",
    "call:proxyCall": "pnpm call src/proxyCall.ts",
    "call:registerDelegate": "pnpm call src/registerDelegate.ts"
  }
}
Source: futureversecom/trn-examples, examples/evm/use-futurepass (deleted) · commit 44c9e16, 2024-10-31 · Apache-2.0, license · part of Code examples

Curated independently. Source-attributed reference for The Root Network. Not affiliated with Futureverse / TRN Labs.