Skip to content

Instantly share code, notes, and snippets.

@shanev
Created September 23, 2021 03:47
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shanev/62acb3c7f38fb99d25fcc5939b3c1afd to your computer and use it in GitHub Desktop.
Save shanev/62acb3c7f38fb99d25fcc5939b3c1afd to your computer and use it in GitHub Desktop.
/*
* Wraps a cw721 contract with helpers.
* Adapted from https://github.com/CosmWasm/cw-plus/blob/main/contracts/cw721-base/helpers.ts.
*/
import {
CosmWasmClient,
SigningCosmWasmClient,
} from '@cosmjs/cosmwasm-stargate';
import { coins } from '@cosmjs/stargate';
export const defaultExecuteFee = {
amount: coins(3000, process.env.NEXT_PUBLIC_DEFAULT_GAS_DENOM || 'ustarx'),
gas: process.env.NEXT_PUBLIC_DEFAULT_GAS_FEE || '333333',
};
export type TokenId = string;
export interface Balances {
readonly address: string;
readonly amount: string; // decimal as string
}
export interface MintInfo {
readonly minter: string;
readonly cap?: string; // decimal as string
}
export interface ContractInfo {
readonly name: string;
readonly symbol: string;
}
export interface NftInfo {
readonly name: string;
readonly description: string;
readonly image: any;
}
export interface Access {
readonly owner: string;
readonly approvals: [];
}
export interface AllNftInfo {
readonly access: Access;
readonly info: NftInfo;
}
export interface Operators {
readonly operators: [];
}
export interface Count {
readonly count: number;
}
// Better to use this interface?
export interface MintMsg {
readonly token_id: TokenId;
readonly owner: string;
readonly name: string;
readonly description?: string;
readonly image?: string;
}
export type Expiration =
| { readonly at_height: number }
| { readonly at_time: number }
| { readonly never: {} };
export interface AllowanceResponse {
readonly allowance: string; // integer as string
readonly expires: Expiration;
}
export interface AllowanceInfo {
readonly allowance: string; // integer as string
readonly spender: string; // bech32 address
readonly expires: Expiration;
}
export interface AllAllowancesResponse {
readonly allowances: readonly AllowanceInfo[];
}
export interface AllAccountsResponse {
// list of bech32 address that have a balance
readonly accounts: readonly string[];
}
export interface TokensResponse {
readonly tokens: readonly string[];
}
export interface CW721ReadOnlyInstance {
readonly contractAddress: string;
// queries
allowance: (owner: string, spender: string) => Promise<AllowanceResponse>;
allAllowances: (
owner: string,
startAfter?: string,
limit?: number
) => Promise<AllAllowancesResponse>;
allAccounts: (
startAfter?: string,
limit?: number
) => Promise<readonly string[]>;
minter: () => Promise<MintInfo>;
contractInfo: () => Promise<ContractInfo>;
nftInfo: (tokenId: TokenId) => Promise<NftInfo>;
allNftInfo: (tokenId: TokenId) => Promise<AllNftInfo>;
ownerOf: (tokenId: TokenId) => Promise<Access>;
approvedForAll: (
owner: string,
include_expired?: boolean,
start_after?: string,
limit?: number
) => Promise<Operators>;
numTokens: () => Promise<Count>;
tokens: (
owner: string,
startAfter?: string,
limit?: number
) => Promise<TokensResponse>;
allTokens: (startAfter?: string, limit?: number) => Promise<TokensResponse>;
}
export interface CW721Instance extends CW721ReadOnlyInstance {
// actions
mint: (
sender: string,
tokenId: TokenId,
owner: string,
name: string,
level: number,
description?: string,
image?: string
) => Promise<string>;
transferNft: (
sender: string,
recipient: string,
tokenId: TokenId
) => Promise<string>;
sendNft: (
sender: string,
contract: string,
token_id: TokenId,
msg?: BinaryType
) => Promise<string>;
approve: (
spender: string,
tokenId: TokenId,
expires?: Expiration
) => Promise<string>;
approveAll: (
sender: string,
operator: string,
expires?: Expiration
) => Promise<string>;
revoke: (spender: string, tokenId: TokenId) => Promise<string>;
revokeAll: (sender: string, operator: string) => Promise<string>;
}
interface CW721Contract {
use: (contractAddress: string) => CW721Instance | CW721ReadOnlyInstance;
}
export const CW721 = (
client: SigningCosmWasmClient | CosmWasmClient
): CW721Contract => {
const use = (
contractAddress: string
): CW721Instance | CW721ReadOnlyInstance => {
// queries
const allowance = async (
owner: string,
spender: string
): Promise<AllowanceResponse> => {
return client.queryContractSmart(contractAddress, {
allowance: { owner, spender },
});
};
const allAllowances = async (
owner: string,
startAfter?: string,
limit?: number
): Promise<AllAllowancesResponse> => {
return client.queryContractSmart(contractAddress, {
all_allowances: { owner, start_after: startAfter, limit },
});
};
const allAccounts = async (
startAfter?: string,
limit?: number
): Promise<readonly string[]> => {
const accounts: AllAccountsResponse = await client.queryContractSmart(
contractAddress,
{ all_accounts: { start_after: startAfter, limit } }
);
return accounts.accounts;
};
const minter = async (): Promise<MintInfo> => {
return client.queryContractSmart(contractAddress, { minter: {} });
};
const contractInfo = async (): Promise<ContractInfo> => {
return client.queryContractSmart(contractAddress, { contract_info: {} });
};
const nftInfo = async (token_id: TokenId): Promise<NftInfo> => {
return client.queryContractSmart(contractAddress, {
nft_info: { token_id },
});
};
const allNftInfo = async (token_id: TokenId): Promise<AllNftInfo> => {
return client.queryContractSmart(contractAddress, {
all_nft_info: { token_id },
});
};
const ownerOf = async (token_id: TokenId): Promise<Access> => {
return await client.queryContractSmart(contractAddress, {
owner_of: { token_id },
});
};
const approvedForAll = async (
owner: string,
include_expired?: boolean,
start_after?: string,
limit?: number
): Promise<Operators> => {
return await client.queryContractSmart(contractAddress, {
approved_for_all: { owner, include_expired, start_after, limit },
});
};
// total number of tokens issued
const numTokens = async (): Promise<Count> => {
return client.queryContractSmart(contractAddress, { num_tokens: {} });
};
// list all token_ids that belong to a given owner
const tokens = async (
owner: string,
start_after?: string,
limit?: number
): Promise<TokensResponse> => {
return client.queryContractSmart(contractAddress, {
tokens: { owner, start_after, limit },
});
};
const allTokens = async (
start_after?: string,
limit?: number
): Promise<TokensResponse> => {
return client.queryContractSmart(contractAddress, {
all_tokens: { start_after, limit },
});
};
// actions
if (client instanceof SigningCosmWasmClient) {
const mint = async (
sender: string,
token_id: TokenId,
owner: string,
name: string,
level: number,
description?: string,
image?: string
): Promise<string> => {
const result = await client.execute(
sender,
contractAddress,
{
mint: { token_id, owner, name, level, description, image },
},
defaultExecuteFee
);
return result.transactionHash;
};
// transfers ownership, returns transactionHash
const transferNft = async (
sender: string,
recipient: string,
token_id: TokenId
): Promise<string> => {
const result = await client.execute(
sender,
contractAddress,
{
transfer_nft: { recipient, token_id },
},
defaultExecuteFee
);
return result.transactionHash;
};
// sends an nft token to another contract (TODO: msg type any needs to be revisited once receiveNft is implemented)
const sendNft = async (
sender: string,
contract: string,
token_id: TokenId,
msg?: any
): Promise<string> => {
const result = await client.execute(
sender,
contractAddress,
{
send_nft: { contract, token_id, msg },
},
defaultExecuteFee
);
return result.transactionHash;
};
const approve = async (
spender: string,
token_id: TokenId,
expires?: Expiration
): Promise<string> => {
const result = await client.execute(
spender,
contractAddress,
{
approve: { spender, token_id, expires },
},
defaultExecuteFee
);
return result.transactionHash;
};
const approveAll = async (
sender: string,
operator: string,
expires?: Expiration
): Promise<string> => {
const result = await client.execute(
sender,
contractAddress,
{
approve_all: { operator, expires },
},
defaultExecuteFee
);
return result.transactionHash;
};
const revoke = async (
spender: string,
token_id: TokenId
): Promise<string> => {
const result = await client.execute(
spender,
contractAddress,
{
revoke: { spender, token_id },
},
defaultExecuteFee
);
return result.transactionHash;
};
const revokeAll = async (
sender: string,
operator: string
): Promise<string> => {
const result = await client.execute(
sender,
contractAddress,
{
revoke_all: { operator },
},
defaultExecuteFee
);
return result.transactionHash;
};
return {
contractAddress,
allowance,
allAllowances,
allAccounts,
minter,
contractInfo,
nftInfo,
allNftInfo,
ownerOf,
approvedForAll,
numTokens,
tokens,
allTokens,
mint,
transferNft,
sendNft,
approve,
approveAll,
revoke,
revokeAll,
};
}
return {
contractAddress,
allowance,
allAllowances,
allAccounts,
minter,
contractInfo,
nftInfo,
allNftInfo,
ownerOf,
approvedForAll,
numTokens,
tokens,
allTokens,
};
};
return { use };
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment