stargaze-studio/contracts/sg721/contract.ts
2023-01-20 16:44:42 +03:00

846 lines
22 KiB
TypeScript

import type { MsgExecuteContractEncodeObject, SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate'
import { toBase64, toUtf8 } from '@cosmjs/encoding'
import type { Coin, logs } from '@cosmjs/stargate'
import { coin } from '@cosmjs/stargate'
import { MsgExecuteContract } from 'cosmjs-types/cosmwasm/wasm/v1/tx'
import type { RoyaltyInfo } from '../vendingMinter/contract'
export interface InstantiateResponse {
readonly contractAddress: string
readonly transactionHash: string
}
export interface MigrateResponse {
readonly transactionHash: string
readonly logs: readonly logs.Log[]
}
export type Expiration = { at_height: number } | { at_time: string } | { never: Record<string, never> }
export interface CollectionInfo {
description?: string
image?: string
external_link?: string
explicit_content?: boolean
royalty_info?: RoyaltyInfo | undefined
}
export interface SG721Instance {
readonly contractAddress: string
// queries
ownerOf: (tokenId: string, includeExpired?: boolean | null) => Promise<any>
approval: (tokenId: string, spender: string, includeExpired?: boolean | null) => Promise<any>
approvals: (tokenId: string, includeExpired?: boolean | null) => Promise<any>
allOperators: (
owner: string,
includeExpired?: boolean | null,
startAfter?: string | null,
limit?: number | null,
) => Promise<any>
numTokens: () => Promise<any>
contractInfo: () => Promise<any>
nftInfo: (tokenId: string) => Promise<any>
allNftInfo: (tokenId: string, includeExpired?: boolean | null) => Promise<any>
tokens: (owner: string, startAfter?: string | null, limit?: number | null) => Promise<any>
allTokens: (startAfter?: string | null, limit?: number | null) => Promise<any>
minter: () => Promise<any>
collectionInfo: () => Promise<any>
//Execute
transferNft: (recipient: string, tokenId: string) => Promise<string>
/// Send is a base message to transfer a token to a contract and trigger an action
/// on the receiving contract.
sendNft: (
contract: string,
tokenId: string,
msg: Record<string, unknown>, //Binary
) => Promise<string>
/// Allows operator to transfer / send the token from the owner's account.
/// If expiration is set, then this allowance has a time/height limit
approve: (spender: string, tokenId: string, expires?: Expiration) => Promise<string>
/// Remove previously granted Approval
revoke: (spender: string, tokenId: string) => Promise<string>
/// Allows operator to transfer / send any token from the owner's account.
/// If expiration is set, then this allowance has a time/height limit
approveAll: (operator: string, expires?: Expiration) => Promise<string>
/// Remove previously granted ApproveAll permission
revokeAll: (operator: string) => Promise<string>
/// Mint a new NFT, can only be called by the contract minter
mint: (tokenId: string, owner: string, tokenURI?: string) => Promise<string> //MintMsg<T>
updateCollectionInfo: (collectionInfo: CollectionInfo) => Promise<string>
freezeCollectionInfo: () => Promise<string>
/// Burn an NFT the sender has access to
burn: (tokenId: string) => Promise<string>
batchBurn: (tokenIds: string) => Promise<string>
batchTransfer: (recipient: string, tokenIds: string) => Promise<string>
}
export interface Sg721Messages {
transferNft: (recipient: string, tokenId: string) => TransferNFTMessage
sendNft: (contract: string, tokenId: string, msg: Record<string, unknown>) => SendNFTMessage
approve: (recipient: string, tokenId: string, expires?: Expiration) => ApproveMessage
revoke: (recipient: string, tokenId: string) => RevokeMessage
approveAll: (operator: string, expires?: Expiration) => ApproveAllMessage
revokeAll: (operator: string) => RevokeAllMessage
mint: (tokenId: string, owner: string, tokenURI?: string) => MintMessage
burn: (tokenId: string) => BurnMessage
batchBurn: (tokenIds: string) => BatchBurnMessage
batchTransfer: (recipient: string, tokenIds: string) => BatchTransferMessage
updateCollectionInfo: (collectionInfo: CollectionInfo) => UpdateCollectionInfoMessage
freezeCollectionInfo: () => FreezeCollectionInfoMessage
}
export interface TransferNFTMessage {
sender: string
contract: string
msg: {
transfer_nft: {
recipient: string
token_id: string
}
}
funds: Coin[]
}
export interface SendNFTMessage {
sender: string
contract: string
msg: {
send_nft: {
contract: string
token_id: string
msg: Record<string, unknown>
}
}
funds: Coin[]
}
export interface ApproveMessage {
sender: string
contract: string
msg: {
approve: {
spender: string
token_id: string
expires?: Expiration
}
}
funds: Coin[]
}
export interface RevokeMessage {
sender: string
contract: string
msg: {
revoke: {
spender: string
token_id: string
}
}
funds: Coin[]
}
export interface ApproveAllMessage {
sender: string
contract: string
msg: {
approve_all: {
operator: string
expires?: Expiration
}
}
funds: Coin[]
}
export interface RevokeAllMessage {
sender: string
contract: string
msg: {
revoke_all: {
operator: string
}
}
funds: Coin[]
}
export interface MintMessage {
sender: string
contract: string
msg: {
mint: {
token_id: string
owner: string
token_uri?: string
}
}
funds: Coin[]
}
export interface BurnMessage {
sender: string
contract: string
msg: {
burn: {
token_id: string
}
}
funds: Coin[]
}
export interface BatchBurnMessage {
sender: string
contract: string
msg: Record<string, unknown>[]
funds: Coin[]
}
export interface BatchTransferMessage {
sender: string
contract: string
msg: Record<string, unknown>[]
funds: Coin[]
}
export interface UpdateCollectionInfoMessage {
sender: string
contract: string
msg: {
update_collection_info: {
collection_info: CollectionInfo
}
}
funds: Coin[]
}
export interface FreezeCollectionInfoMessage {
sender: string
contract: string
msg: { freeze_collection_info: Record<string, never> }
funds: Coin[]
}
export interface SG721Contract {
instantiate: (
senderAddress: string,
codeId: number,
initMsg: Record<string, unknown>,
label: string,
admin?: string,
) => Promise<InstantiateResponse>
migrate: (
senderAddress: string,
contractAddress: string,
codeId: number,
migrateMsg: Record<string, unknown>,
) => Promise<MigrateResponse>
use: (contractAddress: string) => SG721Instance
messages: (contractAddress: string) => Sg721Messages
}
export const SG721 = (client: SigningCosmWasmClient, txSigner: string): SG721Contract => {
const use = (contractAddress: string): SG721Instance => {
const jsonToBinary = (json: Record<string, unknown>): string => {
return toBase64(toUtf8(JSON.stringify(json)))
}
const ownerOf = async (tokenId: string, includeExpired?: boolean | null): Promise<any> => {
const res = await client.queryContractSmart(contractAddress, {
owner_of: { token_id: tokenId, include_expired: includeExpired },
})
return res
}
const approval = async (tokenId: string, spender: string, includeExpired?: boolean | null): Promise<any> => {
const res = await client.queryContractSmart(contractAddress, {
approval: { token_id: tokenId, spender, include_expired: includeExpired },
})
return res
}
const approvals = async (tokenId: string, includeExpired?: boolean | null): Promise<any> => {
const res = await client.queryContractSmart(contractAddress, {
approvals: { token_id: tokenId, include_expired: includeExpired },
})
return res
}
const allOperators = async (
owner: string,
includeExpired?: boolean | null,
startAfter?: string | null,
limit?: number | null,
): Promise<any> => {
const res = await client.queryContractSmart(contractAddress, {
all_operators: { owner, include_expired: includeExpired, start_after: startAfter, limit },
})
return res
}
const numTokens = async (): Promise<any> => {
const res = await client.queryContractSmart(contractAddress, {
num_tokens: {},
})
return res
}
const contractInfo = async (): Promise<any> => {
const res = await client.queryContractSmart(contractAddress, {
contract_info: {},
})
return res
}
const nftInfo = async (tokenId: string): Promise<any> => {
const res = await client.queryContractSmart(contractAddress, {
nft_info: { token_id: tokenId },
})
return res
}
const allNftInfo = async (tokenId: string, includeExpired?: boolean | null): Promise<any> => {
const res = await client.queryContractSmart(contractAddress, {
all_nft_info: { token_id: tokenId, include_expired: includeExpired },
})
return res
}
const tokens = async (owner: string, startAfter?: string | null, limit?: number | null): Promise<any> => {
const res = await client.queryContractSmart(contractAddress, {
tokens: { owner, start_after: startAfter, limit },
})
return res
}
const allTokens = async (startAfter?: string | null, limit?: number | null): Promise<any> => {
const res = await client.queryContractSmart(contractAddress, {
all_tokens: { start_after: startAfter, limit },
})
return res
}
const minter = async (): Promise<any> => {
const res = await client.queryContractSmart(contractAddress, {
minter: {},
})
return res
}
const collectionInfo = async (): Promise<any> => {
const res = await client.queryContractSmart(contractAddress, {
collection_info: {},
})
return res
}
//Execute
const transferNft = async (recipient: string, tokenId: string): Promise<string> => {
const res = await client.execute(
txSigner,
contractAddress,
{
transfer_nft: { recipient, token_id: tokenId },
},
'auto',
'',
)
return res.transactionHash
}
const sendNft = async (
contract: string,
tokenId: string,
msg: Record<string, unknown>, //Binary
): Promise<string> => {
const res = await client.execute(
txSigner,
contractAddress,
{
send_nft: { contract, token_id: tokenId, msg: jsonToBinary(msg) },
},
'auto',
'',
)
return res.transactionHash
}
const approve = async (spender: string, tokenId: string, expires?: Expiration): Promise<string> => {
const res = await client.execute(
txSigner,
contractAddress,
{
approve: { spender, token_id: tokenId, expires },
},
'auto',
'',
)
return res.transactionHash
}
const revoke = async (spender: string, tokenId: string): Promise<string> => {
const res = await client.execute(
txSigner,
contractAddress,
{
revoke: { spender, token_id: tokenId },
},
'auto',
'',
)
return res.transactionHash
}
const approveAll = async (operator: string, expires?: Expiration): Promise<string> => {
const res = await client.execute(
txSigner,
contractAddress,
{
approve_all: { operator, expires },
},
'auto',
'',
)
return res.transactionHash
}
const revokeAll = async (operator: string): Promise<string> => {
const res = await client.execute(
txSigner,
contractAddress,
{
revoke_all: { operator },
},
'auto',
'',
)
return res.transactionHash
}
const mint = async (tokenId: string, owner: string, tokenURI?: string): Promise<string> => {
const res = await client.execute(
txSigner,
contractAddress,
{
mint: {
token_id: tokenId,
owner,
token_uri: tokenURI,
},
},
'auto',
'',
)
return res.transactionHash
}
const burn = async (tokenId: string): Promise<string> => {
const res = await client.execute(
txSigner,
contractAddress,
{
burn: { token_id: tokenId },
},
'auto',
'',
)
return res.transactionHash
}
const batchBurn = async (tokenIds: string): Promise<string> => {
const executeContractMsgs: MsgExecuteContractEncodeObject[] = []
if (tokenIds.includes(':')) {
const [start, end] = tokenIds.split(':').map(Number)
for (let i = start; i <= end; i++) {
const msg = {
burn: { token_id: i.toString() },
}
const executeContractMsg: MsgExecuteContractEncodeObject = {
typeUrl: '/cosmwasm.wasm.v1.MsgExecuteContract',
value: MsgExecuteContract.fromPartial({
sender: txSigner,
contract: contractAddress,
msg: toUtf8(JSON.stringify(msg)),
}),
}
executeContractMsgs.push(executeContractMsg)
}
} else {
const tokenNumbers = tokenIds.split(',').map(Number)
for (let i = 0; i < tokenNumbers.length; i++) {
const msg = {
burn: { token_id: tokenNumbers[i].toString() },
}
const executeContractMsg: MsgExecuteContractEncodeObject = {
typeUrl: '/cosmwasm.wasm.v1.MsgExecuteContract',
value: MsgExecuteContract.fromPartial({
sender: txSigner,
contract: contractAddress,
msg: toUtf8(JSON.stringify(msg)),
}),
}
executeContractMsgs.push(executeContractMsg)
}
}
const res = await client.signAndBroadcast(txSigner, executeContractMsgs, 'auto', 'batch burn')
return res.transactionHash
}
const batchTransfer = async (recipient: string, tokenIds: string): Promise<string> => {
const executeContractMsgs: MsgExecuteContractEncodeObject[] = []
if (tokenIds.includes(':')) {
const [start, end] = tokenIds.split(':').map(Number)
for (let i = start; i <= end; i++) {
const msg = {
transfer_nft: { recipient, token_id: i.toString() },
}
const executeContractMsg: MsgExecuteContractEncodeObject = {
typeUrl: '/cosmwasm.wasm.v1.MsgExecuteContract',
value: MsgExecuteContract.fromPartial({
sender: txSigner,
contract: contractAddress,
msg: toUtf8(JSON.stringify(msg)),
}),
}
executeContractMsgs.push(executeContractMsg)
}
} else {
const tokenNumbers = tokenIds.split(',').map(Number)
for (let i = 0; i < tokenNumbers.length; i++) {
const msg = {
transfer_nft: { recipient, token_id: tokenNumbers[i].toString() },
}
const executeContractMsg: MsgExecuteContractEncodeObject = {
typeUrl: '/cosmwasm.wasm.v1.MsgExecuteContract',
value: MsgExecuteContract.fromPartial({
sender: txSigner,
contract: contractAddress,
msg: toUtf8(JSON.stringify(msg)),
}),
}
executeContractMsgs.push(executeContractMsg)
}
}
const res = await client.signAndBroadcast(txSigner, executeContractMsgs, 'auto', 'batch transfer')
return res.transactionHash
}
// eslint-disable-next-line @typescript-eslint/no-shadow
const updateCollectionInfo = async (collectionInfo: CollectionInfo): Promise<string> => {
const res = await client.execute(
txSigner,
contractAddress,
{
update_collection_info: { collection_info: collectionInfo },
},
'auto',
'',
)
return res.transactionHash
}
const freezeCollectionInfo = async (): Promise<string> => {
const res = await client.execute(
txSigner,
contractAddress,
{
freeze_collection_info: {},
},
'auto',
'',
)
return res.transactionHash
}
return {
contractAddress,
ownerOf,
approval,
approvals,
allOperators,
numTokens,
contractInfo,
nftInfo,
allNftInfo,
tokens,
allTokens,
minter,
collectionInfo,
transferNft,
sendNft,
approve,
revoke,
approveAll,
revokeAll,
mint,
burn,
batchBurn,
batchTransfer,
updateCollectionInfo,
freezeCollectionInfo,
}
}
const instantiate = async (
senderAddress: string,
codeId: number,
initMsg: Record<string, unknown>,
label: string,
admin?: string,
): Promise<InstantiateResponse> => {
const result = await client.instantiate(senderAddress, codeId, initMsg, label, 'auto', {
funds: [coin('3000000000', 'ustars')],
memo: '',
admin,
})
return {
contractAddress: result.contractAddress,
transactionHash: result.transactionHash,
}
}
const migrate = async (
senderAddress: string,
contractAddress: string,
codeId: number,
migrateMsg: Record<string, unknown>,
): Promise<MigrateResponse> => {
const result = await client.migrate(senderAddress, contractAddress, codeId, migrateMsg, 'auto')
return {
transactionHash: result.transactionHash,
logs: result.logs,
}
}
const messages = (contractAddress: string) => {
const transferNft = (recipient: string, tokenId: string) => {
return {
sender: txSigner,
contract: contractAddress,
msg: {
transfer_nft: {
recipient,
token_id: tokenId,
},
},
funds: [],
}
}
const sendNft = (contract: string, tokenId: string, msg: Record<string, unknown>) => {
return {
sender: txSigner,
contract: contractAddress,
msg: {
send_nft: {
contract,
token_id: tokenId,
msg,
},
},
funds: [],
}
}
const approve = (spender: string, tokenId: string, expires?: Expiration) => {
return {
sender: txSigner,
contract: contractAddress,
msg: {
approve: {
spender,
token_id: tokenId,
expires,
},
},
funds: [],
}
}
const revoke = (spender: string, tokenId: string) => {
return {
sender: txSigner,
contract: contractAddress,
msg: {
revoke: {
spender,
token_id: tokenId,
},
},
funds: [],
}
}
const approveAll = (operator: string, expires?: Expiration) => {
return {
sender: txSigner,
contract: contractAddress,
msg: {
approve_all: {
operator,
expires,
},
},
funds: [],
}
}
const revokeAll = (operator: string) => {
return {
sender: txSigner,
contract: contractAddress,
msg: {
revoke_all: {
operator,
},
},
funds: [],
}
}
const mint = (tokenId: string, owner: string, tokenURI?: string) => {
return {
sender: txSigner,
contract: contractAddress,
msg: {
mint: {
token_id: tokenId,
owner,
token_uri: tokenURI,
},
},
funds: [],
}
}
const burn = (tokenId: string) => {
return {
sender: txSigner,
contract: contractAddress,
msg: {
burn: {
token_id: tokenId,
},
},
funds: [],
}
}
const batchBurn = (tokenIds: string): BatchBurnMessage => {
const msg: Record<string, unknown>[] = []
if (tokenIds.includes(':')) {
const [start, end] = tokenIds.split(':').map(Number)
for (let i = start; i <= end; i++) {
msg.push({
burn: { token_id: i.toString() },
})
}
} else {
const tokenNumbers = tokenIds.split(',').map(Number)
for (let i = 0; i < tokenNumbers.length; i++) {
msg.push({ burn: { token_id: tokenNumbers[i].toString() } })
}
}
return {
sender: txSigner,
contract: contractAddress,
msg,
funds: [],
}
}
const batchTransfer = (recipient: string, tokenIds: string): BatchTransferMessage => {
const msg: Record<string, unknown>[] = []
if (tokenIds.includes(':')) {
const [start, end] = tokenIds.split(':').map(Number)
for (let i = start; i <= end; i++) {
msg.push({
trasnfer_nft: { recipient, token_id: i.toString() },
})
}
} else {
const tokenNumbers = tokenIds.split(',').map(Number)
for (let i = 0; i < tokenNumbers.length; i++) {
msg.push({ trasnfer_nft: { recipient, token_id: tokenNumbers[i].toString() } })
}
}
return {
sender: txSigner,
contract: contractAddress,
msg,
funds: [],
}
}
const updateCollectionInfo = (collectionInfo: CollectionInfo) => {
return {
sender: txSigner,
contract: contractAddress,
msg: {
update_collection_info: { collection_info: collectionInfo },
},
funds: [],
}
}
const freezeCollectionInfo = () => {
return {
sender: txSigner,
contract: contractAddress,
msg: {
freeze_collection_info: {},
},
funds: [],
}
}
return {
transferNft,
sendNft,
approve,
revoke,
approveAll,
revokeAll,
mint,
burn,
batchBurn,
batchTransfer,
updateCollectionInfo,
freezeCollectionInfo,
}
}
return { use, instantiate, migrate, messages }
}