Documentation Index
Fetch the complete documentation index at: https://developers.circle.com/llms.txt
Use this file to discover all available pages before exploring further.
This tutorial walks you through creating a developer-controlled wallet to sign
transactions on NEAR.
Prerequisites
Before you begin:
Step 1. Create NEAR wallets
When
creating a developer-controlled wallet,
pass NEAR-TESTNET or NEAR in the blockchains field. This wallet can be
used to sign transactions on NEAR.
The following example code shows how to create a wallet using the Circle
Developer SDK.
const response = await circleDeveloperSdk.createWallets({
accountType: "EOA",
blockchains: ["NEAR-TESTNET"],
count: 2,
walletSetId: "<wallet-set-id>",
});
You must send NEAR tokens to activate the wallet. Without NEAR tokens in the
wallet, you will receive an error stating that the account does not exist when
broadcasting a transaction.
Use count to specify the number of wallets to create, up to a maximum of 200
per API request.
Step 2. Build and sign a raw transaction
To build a raw transaction for signing in JavaScript:
- Prepare the transaction object - To build a NEAR transaction, you must
define several parameters, including the sender, receiver, amount, actions,
nonce, and any additional metadata required.
- Serialize the transaction object with Borsh and encode it with Base64 -
NEAR uses Borsh serialization for transaction objects, so you need to
serialize the constructed transaction and then encode it to Base64 before
signing.
The following example code shows how to build a raw transaction for signing.
import { initiateDeveloperControlledWalletsClient } from "@circle-fin/developer-controlled-wallets";
import { connect, utils } from "near-api-js";
import { baseDecode, baseEncode } from "@near-js/utils";
import { PublicKey } from "@near-js/crypto";
import {
createTransaction,
actionCreators,
encodeTransaction,
} from "@near-js/transactions";
import axios from "axios";
import fs from "fs";
const NEAR_CONFIG = {
networkId: "testnet",
nodeUrl: "https://rpc.testnet.near.org",
};
// fill in these variables
const CIRCLE_API_KEY = "";
const CIRCLE_ENTITY_SECRET = "";
const CIRCLE_WALLET_PUBLIC_KEY = ""; // with `ed25519:` scheme
const CIRCLE_WALLET_ID = "";
const CIRCLE_TX_MEMO = "";
const MASTER_ACCOUNT = "";
const RECEIVER = "";
const AMOUNT = "0.001";
async function main() {
const client = initiateDeveloperControlledWalletsClient({
apiKey: CIRCLE_API_KEY,
entitySecret: CIRCLE_ENTITY_SECRET,
});
try {
// setup client and account
const near = await connect({ ...NEAR_CONFIG });
const sender = await near.account(MASTER_ACCOUNT);
// get latest finalized block
const block = await near.connection.provider.block({ finality: "final" });
const blockHash = block.header.hash;
// get sender nonce
const senderAccessKey = await sender.getAccessKeys();
let nonce = BigInt(0);
for (let i = 0; i < senderAccessKey.length; i++) {
if (senderAccessKey[i].public_key === CIRCLE_WALLET_PUBLIC_KEY) {
nonce = senderAccessKey[i].access_key.nonce + BigInt(1);
}
}
if (nonce === BigInt(0)) {
console.error(
`couldn't find corresponding access key: ${CIRCLE_WALLET_PUBLIC_KEY}`,
);
return;
}
// build raw transaction
const actions = [
actionCreators.transfer(
BigInt(utils.format.parseNearAmount(AMOUNT) || 0),
),
];
const signerPublicKey = PublicKey.fromString(CIRCLE_WALLET_PUBLIC_KEY);
const transaction = createTransaction(
MASTER_ACCOUNT,
signerPublicKey,
RECEIVER,
nonce,
actions,
baseDecode(blockHash),
);
// serialize borsh
const message = encodeTransaction(transaction);
// base64 encode
const base64EncodedTx = Buffer.from(message).toString("base64");
// call Circle's API to sign tx
const signedTx = await client.signTransaction({
walletId: CIRCLE_WALLET_ID,
rawTransaction: base64EncodedTx,
memo: CIRCLE_TX_MEMO,
});
} catch (err) {
if (axios.isAxiosError(err)) {
console.error(`status: ${err.response?.status}`);
console.error(`data: `, err.response?.data);
} else {
console.error(`unknown err: `, err);
}
}
}
(async () => {
await main();
})();