Skip to content

Commit

Permalink
Wrapped SOL support (solana-labs#416)
Browse files Browse the repository at this point in the history
* Add support for wrapped native token account generation in the Token JS client.

* Some small changes to the token test script as per review comments
  • Loading branch information
dankelleher authored Sep 10, 2020
1 parent 2ffc528 commit ac3004b
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 8 deletions.
24 changes: 17 additions & 7 deletions token/js/cli/token-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from '@solana/web3.js';
import semver from 'semver';

import {Token} from '../client/token';
import {Token, NATIVE_MINT} from '../client/token';
import {url} from '../url';
import {newAccountWithLamports} from '../client/util/new-account-with-lamports';
import {sleep} from '../client/util/sleep';
Expand Down Expand Up @@ -527,19 +527,29 @@ export async function multisig(): Promise<void> {

export async function nativeToken(): Promise<void> {
const connection = await getConnection();

const mintPublicKey = new PublicKey(
'So11111111111111111111111111111111111111112',
);
// this user both pays for the creation of the new token account
// and provides the lamports to wrap
const payer = await newAccountWithLamports(
connection,
100000000000 /* wag */,
);
const token = new Token(connection, mintPublicKey, programId, payer);
const lamportsToWrap = 50000000000;

const token = new Token(connection, NATIVE_MINT, programId, payer);
const owner = new Account();
const native = await token.createAccount(owner.publicKey);
const native = await Token.createWrappedNativeAccount(
connection,
programId,
owner.publicKey,
payer,
lamportsToWrap,
);
let accountInfo = await token.getAccountInfo(native);
assert(accountInfo.isNative);

// check that the new account has wrapped native tokens.
assert(accountInfo.amount.toNumber() === lamportsToWrap);

let balance;
let info = await connection.getAccountInfo(native);
if (info != null) {
Expand Down
77 changes: 76 additions & 1 deletion token/js/client/token.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ const AuthorityTypeCodes = {
CloseAccount: 3,
};

// The address of the special mint for wrapped native token.
export const NATIVE_MINT = new PublicKey(
'So11111111111111111111111111111111111111112',
);

/**
* Information about the mint
*/
Expand Down Expand Up @@ -384,7 +389,7 @@ export class Token {
}

/**
* Create and initializes a new account.
* Create and initialize a new account.
*
* This account may then be used as a `transfer()` or `approve()` destination
*
Expand Down Expand Up @@ -429,6 +434,76 @@ export class Token {
return newAccount.publicKey;
}

/**
* Create an initialize a new account on the special native token mint.
*
* In order to wrap native tokens, the account must have a balance of native tokens
* when it is initialized with the token program.
*
* This function sends lamports to the new account before initializing it.
*
* @param connection A solana web3 connection
* @param programId The token program ID
* @param owner The owner of the new token account
* @param payer The source of the lamports to initialize, and payer of the initialization fees.
* @param amount The amount of lamports to wrap
* @return {Promise<PublicKey>} The new token account
*/
static async createWrappedNativeAccount(
connection: Connection,
programId: PublicKey,
owner: PublicKey,
payer: Account,
amount: number,
): Promise<PublicKey> {
// Allocate memory for the account
const balanceNeeded = await Token.getMinBalanceRentForExemptAccount(
connection,
);

// Create a new account
const newAccount = new Account();
const transaction = SystemProgram.createAccount({
fromPubkey: payer.publicKey,
newAccountPubkey: newAccount.publicKey,
lamports: balanceNeeded,
space: AccountLayout.span,
programId,
});

// Send lamports to it (these will be wrapped into native tokens by the token program)
transaction.add(
SystemProgram.transfer({
fromPubkey: payer.publicKey,
toPubkey: newAccount.publicKey,
lamports: amount,
}),
);

// assign the new account to the native token mint.
// the account will be initialized with a balance equal to the native token balance.
// (i.e. amount)
transaction.add(
Token.createInitAccountInstruction(
programId,
NATIVE_MINT,
newAccount.publicKey,
owner,
),
);

// Send the two instructions
await sendAndConfirmTransaction(
'createAccount, transfer, and initializeAccount',
connection,
transaction,
payer,
newAccount,
);

return newAccount.publicKey;
}

/**
* Create and initializes a new multisig.
*
Expand Down
10 changes: 10 additions & 0 deletions token/js/module.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ declare module '@solana/spl-token' {
| 'FreezeAccount'
| 'AccountOwner'
| 'CloseAccount';

export const NATIVE_MINT: PublicKey;

export type MintInfo = {
mintAuthority: null | PublicKey,
supply: u64,
Expand Down Expand Up @@ -65,6 +68,13 @@ declare module '@solana/spl-token' {
): Promise<Token>;
static getAccount(connection: Connection): Promise<Account>;
createAccount(owner: PublicKey): Promise<PublicKey>;
static createWrappedNativeAccount(
connection: Connection,
programId: PublicKey,
owner: PublicKey,
payer: Account,
amount: number,
): Promise<PublicKey>;
createMultisig(m: number, signers: Array<PublicKey>): Promise<PublicKey>;
getMintInfo(): Promise<MintInfo>;
getAccountInfo(account: PublicKey): Promise<AccountInfo>;
Expand Down
8 changes: 8 additions & 0 deletions token/js/module.flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ declare module '@solana/spl-token' {
| 'FreezeAccount'
| 'AccountOwner'
| 'CloseAccount';
declare export var NATIVE_MINT: PublicKey;
declare export type MintInfo = {|
mintAuthority: null | PublicKey,
supply: u64,
Expand Down Expand Up @@ -68,6 +69,13 @@ declare module '@solana/spl-token' {
): Promise<Token>;
static getAccount(connection: Connection): Promise<Account>;
createAccount(owner: PublicKey): Promise<PublicKey>;
static createWrappedNativeAccount(
connection: Connection,
programId: PublicKey,
owner: PublicKey,
payer: Account,
amount: number,
): Promise<PublicKey>;
createMultisig(m: number, signers: Array<PublicKey>): Promise<PublicKey>;
getMintInfo(): Promise<MintInfo>;
getAccountInfo(account: PublicKey): Promise<AccountInfo>;
Expand Down

0 comments on commit ac3004b

Please sign in to comment.