๋Œ€์™ธํ™œ๋™/ํ˜„์žฅ ์‹ค์Šต

[Aptos] Typescript SDK๋ฅผ ์ด์šฉํ•œ ํ† ํฐ ๋‹ค๋ฃจ๊ธฐ

kite707 2023. 4. 26.

์ด ๊ธ€์€ ์•„๋ž˜ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ๋ณด๊ณ  Aptos์˜ Typescript SDK๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๊ธฐ๋กํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค.

Mint NFTs with the SDKs | Aptos Docs

 

Your First NFT | Aptos Docs

This tutorial describes how to create and transfer NFTs on the Aptos blockchain. The Aptos implementation for core NFTs can be found in the token.move Move module.

aptos.dev

 

์ด ๊ธ€์„ ํ†ตํ•ด ์ฝœ๋ ‰์…˜ ์ƒ์„ฑ(ํ† ํฐ์ด ๋“ค์–ด๊ฐ€๋Š” ๊ณณ), ํ† ํฐ ์ƒ์„ฑ, ํ† ํฐ ์ „์†ก(๋ฏผํŒ…) ๋ฐฉ๋ฒ•์„ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์‚ฌ์ „ ์ค€๋น„

๋จผ์ € ์˜ˆ์ œ ์ฝ”๋“œ๋ฅผ ํด๋ก ๋ฐ›๊ณ , ์ฝ”๋“œ๋ฅผ ์‹คํ–‰์‹œ์ผœ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. vs์ฝ”๋“œ์—์„œ ํด๋”๋ฅผ ๋งŒ๋“  ๋’ค ์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰์‹œ์ผœ์ฃผ์„ธ์š”.

git clone git@github.com:aptos-labs/aptos-core.git

cd aptos-core/ecosystem/typescript/sdk/examples/typescript

pnpm install

pnpm run simple_nft

์œ„ ์ฝ”๋“œ์˜ ์‹คํ–‰ ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

=== Addresses ===
Alice: 0xeef95e86c160fa10a71675c6075f44f8f2c6125f57b4b589424f1fbee385f754
Bob: 0x4dcd7b180c123fdb989d10f71fba6c978bda268c2e3660c169bdb55f67aab776

=== Initial Coin Balances ===
Alice: 100000000
Bob: 100000000

=== Creating Collection and Token ===
Alice's collection: {
    "description": "Alice's simple collection",
    "maximum": "18446744073709551615",
    "mutability_config": {
        "description": false,
        "maximum": false,
        "uri": false
    },
    "name": "Alice's",
    "supply": "1",
    "uri": "<https://alice.com>"
}
Alice's token balance: 1
Alice's token data: {
    "default_properties": {
        "map": {
            "data": []
        }
    },
    "description": "Alice's simple token",
    "largest_property_version": "0",
    "maximum": "18446744073709551615",
    "mutability_config": {
        "description": false,
        "maximum": false,
        "properties": false,
        "royalty": false,
        "uri": false
    },
    "name": "Alice's first token",
    "royalty": {
        "payee_address": "0xeef95e86c160fa10a71675c6075f44f8f2c6125f57b4b589424f1fbee385f754",
        "royalty_points_denominator": "0",
        "royalty_points_numerator": "0"
    },
    "supply": "1",
    "uri": "<https://aptos.dev/img/nyan.jpeg>"
}

=== Transferring the token to Bob ===
Alice's token balance: 0
Bob's token balance: 1

=== Transferring the token back to Alice using MultiAgent ===
Alice's token balance: 1
Bob's token balance: 0

์šฐ์„  ๊ฒฐ๊ณผ๋กœ ๋‚˜์˜จ ๊ฒƒ๋“ค์„ ์ข…ํ•ฉํ•ด๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • REST, Faucet Client๋ฅผ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค.
  • Alice, Bob ๋‘ ๋ช…์˜ ๊ณ„์ •์„ ๋งŒ๋“ ๋‹ค.(The funding and creation of Alice and Bob's accounts.)
  • Collections์„ ๋งŒ๋“ค๊ณ  Alice ์˜ ๊ณ„์ •์—์„œ ํ† ํฐ์„ ์‚ฌ์šฉํ•œ๋‹ค.
  • Alice๋Š” Bob์—๊ฒŒ ํ† ํฐ์„ ์š”์ฒญํ•œ๋‹ค
  • Bob๋Š” multiagent transaction์„ ์ด์šฉํ•ด์„œ bob์—๊ฒŒ ๋ณด๋‚ธ๋‹ค.

๊ทธ๋ฆฌ๊ณ  pnpm run simple_nft๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ–ˆ๋Š”๋ฐ package.json์„ ๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค.

ts-node๋Š” ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ์„ ์‹คํ–‰ํ•ด์ฃผ๋Š” ๋ช…๋ น์–ด์ž…๋‹ˆ๋‹ค. ์ฆ‰ ์šฐ๋ฆฌ๋Š” simple_nft.tsํŒŒ์ผ์„ ์‹คํ–‰์‹œํ‚จ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด์ œ ๋‹จ๊ณ„์— ๋”ฐ๋ผ์„œ ์ฝ”๋“œ๋ฅผ ์ฒœ์ฒœํžˆ ๋ณด๋ฉฐ ์ดํ•ดํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 


ํด๋ผ์ด์–ธํŠธ ๋ฐ ๊ณ„์ • ์ƒ์„ฑ

ํ† ํฐ์„ ๋งŒ๋“ค๊ณ , ์ „์†กํ•˜๋Š” ์ž‘์—…์„ ํ•˜๊ธฐ ์ „, SDK๋ฅผ ์ด์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋ช‡ ๊ฐ€์ง€ ์‚ฌ์ „ ์ž‘์—…์„ ํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. client๋ฅผ ๋งŒ๋“ค๊ณ , ํ† ํฐ์„ ์ƒ์„ฑํ•  ์‚ฌ๋žŒ(alice), ๋ฐ›์„ ์‚ฌ๋žŒ(bob)์„ ๊ฐ๊ฐ ์„ ์–ธํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ฐ client๋“ค์€ SDK์— ๋‚ด์žฅ๋˜์–ด์žˆ๋Š” ํ•จ์ˆ˜๋“ค์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค๋‹ˆ๋‹ค. 

 

API Client๋Š” REST API๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค. Faucet Client๋Š” devnet Faucet Service๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค. ๊ทธ ๊ฒฐ๊ณผ ๊ณ„์ •์„ ์ƒ์„ฑํ•˜๊ณ  ์ฝ”์ธ์„ ๋„ฃ์–ด์ค„ ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ดํ›„ API Client๋ฅผ ์ด์šฉํ•ด Token Client๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์ด Token Client๋Š” collection์ƒ์„ฑ, token์ƒ์„ฑ, token์ „์†ก ๋“ฑ ํ† ํฐ ๊ด€๋ จ ์ž‘์—…์„ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.

// Create API and faucet clients.
  // :!:>section_1a
  const client = new AptosClient(NODE_URL);
  const faucetClient = new FaucetClient(NODE_URL, FAUCET_URL); // <:!:section_1a

  // Create client for working with the token module.
  // :!:>section_1b
  const tokenClient = new TokenClient(client); // <:!:section_1b

  // Create a coin client for checking account balances.
  const coinClient = new CoinClient(client);

 

์ด์ œ aptos๊ณ„์ •์„ 2๊ฐ€์ง€ ๋งŒ๋“ค์–ด ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋˜ ์•ž์„œ ๋งŒ๋“  faucetClient๋ฅผ ํ†ตํ•ด ์ฝ”์ธ์„ ๋„ฃ์–ด์ฃผ๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. faucetClient๋ฅผ ํ†ตํ•ด ์ฝ”์ธ์„ ๋„ฃ์–ด์ฃผ๊ธฐ ์ด์ „์—๋Š” off-chain ๊ณ„์ •์ด๋ผ์„œ ํ† ํฐ์„ ๋ณด๋‚ด๋ ค ํ•ด๋„ ์ฐพ์„ ์ˆ˜ ์—†๋Š” ๊ณ„์ •์ด๋ผ๊ณ  ๋‚˜์˜ต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋„ฃ์–ด์ค€ ์ฝ”์ธ์€ ํŠธ๋žœ์ ์…˜(ํ† ํฐ์„ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜, ๋ณด๋‚ด๋Š” ๋“ฑ์˜ ๋ธ”๋ก์ฒด์ธ๊ณผ ์ƒํ˜ธ์ž‘์šฉ ํ•˜๋Š” ์ž‘์—…)์„ ํ•˜๋Š” ๋ฐ์— ์†Œ๋น„๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”์ธ์„ ๋„ฃ์–ด์ฃผ๋„๋ก ํ•ฉ์‹œ๋‹ค.

const alice = new AptosAccount();
const bob = new AptosAccount(); 

await faucetClient.fundAccount(alice.address(), 100_000_000);
await faucetClient.fundAccount(bob.address(), 100_000_000);

 

์ฝœ๋ ‰์…˜ ๋ฐ ํ† ํฐ ์ƒ์„ฑ

์ด์ œ ์ฝœ๋ ‰์…˜๊ณผ ํ† ํฐ์„ ์ƒ์„ฑํ•ด์ฃผ๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์•ฑํ† ์Šค์—์„œ๋Š” ํ† ํฐ์„ ๋งŒ๋“œ๋ ค๋Š” ์‚ฌ๋žŒ์ด ‘Collection’์ด๋ผ๋Š” ๊ฒƒ์„ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด Collection์— ํ† ํฐ๋“ค์„ ์ €์žฅํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. CreateCollection์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฐ’๋“ค์„ ๋ฐ›์Šต๋‹ˆ๋‹ค.

async createCollection(
  account: AptosAccount,
  name: string,
  description: string,
  uri: string,
  maxAmount?: AnyNumber = MAX_U64_BIG_INT,
  extraArgs?: OptionalTransactionArgs,
): Promise<string> {}

์ฝœ๋ ‰์…˜์„ ์ƒ์„ฑํ•˜๋Š” ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

const txnHash1 = await tokenClient.createCollection(
    alice,
    collectionName,
    "Alice's simple collection",
    "<https://alice.com>",
  );
  await client.waitForTransaction(txnHash1, { checkSuccess: true });

waitForTransaction์€ ํŠธ๋žœ์žญ์…˜ ํ•ด์‰ฌ์˜ ๊ฐ’์„ ๋ฐ›์•„ ์ƒํƒœ๊ฐ€ true๊ฐ€ ๋˜๋ฉด ๋„˜์–ด๊ฐ€๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

 

์ด์ œ๋Š” ๋งŒ๋“  ์ฝœ๋ ‰์…˜์— ํ† ํฐ์„ ๋งŒ๋“ค์–ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์•ฑํ† ์Šค์— ์˜ํ•˜๋ฉด ํ† ํฐ์„ ์ƒ์„ฑํ•˜๋Š” ํ•จ์ˆ˜๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ์ธ์ž๋ฅผ ๋ฐ›๋Š”๋‹ค๊ณ  ๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค.

async createToken(
  account: AptosAccount,
  collectionName: string,
  name: string,
  description: string,
  supply: number,
  uri: string,
  max: AnyNumber = MAX_U64_BIG_INT,
  royalty_payee_address: MaybeHexString = account.address(),
  royalty_points_denominator: number = 0,
  royalty_points_numerator: number = 0,
  property_keys: Array<string> = [],
  property_values: Array<string> = [],
  property_types: Array<string> = [],
  extraArgs?: OptionalTransactionArgs,
): Promise<string> {

๊ณต์‹ ์˜ˆ์ œ์—์„œ๋Š” ์ตœ์†Œํ•œ์˜ ์ธ์ž๋ฅผ ์ฃผ๊ณ  ์•„๋ž˜์™€ ๊ฐ™์ด ํ† ํฐ์„ ์ƒ์„ฑํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

const txnHash2 =await tokenClient.createToken(
  alice, //account
  collectionName, //collectionName
  tokenName,  //tokenName
  "Alice's simple token", //description
  1, //supply(๋ฐœํ–‰๋Ÿ‰)
  "<https://aptos.dev/img/nyan.jpeg>", //uri: ์‚ฌ์ง„์ด๋ฏธ์ง€
);

์ด์ œ ์ฝœ๋ ‰์…˜, ํ† ํฐ, ๊ณ„์ • ๋“ฑ์ด ์ž˜ ์ƒ์„ฑ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ๋“ค์€ ํŠน์ • collection, token์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

const collectionData = await tokenClient.getCollectionData(alice.address(), collectionName);
console.log(`Alice's collection: ${JSON.stringify(collectionData, null, 4)}`);

const tokenData = await tokenClient.getTokenData(alice.address(), collectionName, tokenName);
console.log(`Alice's token data: ${JSON.stringify(tokenData, null, 4)}`);

์ด์ œ ํŠน์ • ์ง€๊ฐ‘์˜ ํ† ํฐ ์ž”๊ณ ๋ฅผ ์ฝ์–ด์˜ค๋Š” ์ฝ”๋“œ๋ฅผ ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์•ž์„œ await faucetClient.fundAccount(alice.address(), 100_000_000); ๋ฅผ ํ•ด์คฌ๊ธฐ ๋•Œ๋ฌธ์— 100000000์ด ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.

const aliceBalance1 = await tokenClient.getToken(
  alice.address(),
  collectionName,
  tokenName,
  `${tokenPropertyVersion}`,
);
console.log(`Alice's token balance: ${aliceBalance1["amount"]}`);

 

ํ† ํฐ ์ „์†กํ•˜๊ธฐ 1 (Offer & Claim)

์ด์ œ alice -> bob์œผ๋กœ ํ† ํฐ์„ ์ „์†กํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์•ฑํ† ์Šค๋Š” ํ† ํฐ์„ ๋ฐ›๋Š” ์‚ฌ๋žŒ์ด ์›์น˜ ์•Š๋Š” ํ† ํฐ์„ ๋ฐ›๋Š” ์ƒํ™ฉ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด offer → claim, 2๊ฐ€์ง€ ๋‹จ๊ณ„๋กœ ํ† ํฐ์„ ์ „๋‹ฌ(Transfer)ํ•˜๋Š” ๊ณผ์ •์„ ๋‚˜๋ˆ ๋†“์•˜์Šต๋‹ˆ๋‹ค. ๊ฐ ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

const txnHash3 = await tokenClient.offerToken(
  alice,  //์ „๋‹ฌ์ž
  bob.address(), //์ˆ˜๋ น์ž ์ฃผ์†Œ
  alice.address(), //ํ† ํฐ ์ƒ์„ฑ์ž ์ฃผ์†Œ
  collectionName,
  tokenName,
  1, //์ „๋‹ฌ ์ˆ˜๋Ÿ‰
  tokenPropertyVersion,
);

const txnHash4 = await tokenClient.claimToken(
  bob,  //์ˆ˜๋ น์ž
  alice.address(), //์ „๋‹ฌ์ž ์ฃผ์†Œ
  alice.address(), //ํ† ํฐ ์ƒ์„ฑ์ž ์ฃผ์†Œ
  collectionName,
  tokenName,
  tokenPropertyVersion,
);

 

ํ† ํฐ ์ „์†กํ•˜๊ธฐ 2 (directTransferToken)

์„œ๋น„์Šค์— ๋”ฐ๋ผ ์œ„ ๊ณผ์ •์„ ๋‚˜๋ˆ„๊ณ  ์‹ถ์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์•ฑํ† ์Šค sdk์—์„œ๋Š” directTransferToken์ด๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜๋ฉด offer, claim์„ ํ•œ๋ฒˆ์— ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

let txnHash5 = await tokenClient.directTransferToken(
  bob,   //์ „๋‹ฌ์ž
  alice, //์ˆ˜๋ น์ž
  alice.address(), //์ƒ์„ฑ์ž ์ฃผ์†Œ
  collectionName,
  tokenName,
  1, //์ „๋‹ฌ ์ˆ˜๋Ÿ‰
  tokenPropertyVersion,
);

 

๋‹ค๋งŒ ๋ฐ›๋Š” ์‚ฌ๋žŒ(receiver)์˜ ๊ณ„์ •์„ aptosAccountํ˜•์‹์œผ๋กœ ๋„˜๊ฒจ์ค˜์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ›๋Š”์‚ฌ๋žŒ์˜ private key๋ฅผ ์•Œ๊ณ  ์žˆ๋‹ค๋Š” ์ „์ œํ•˜์— ์œ„ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ง€๊ธˆ ํ•˜๋Š” ๋‚ด์šฉ์€ ๊ณต์‹๋ฌธ์„œ์—์„œ ์ง„ํ–‰ํ•˜๋Š” ์˜ˆ์ œ ์ฝ”๋“œ์ด๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์ „์˜ bob = new AptosClient()๋ผ๊ณ  ์ง€์ •ํ•ด๋‘์—ˆ์ง€๋งŒ(์ž„์˜์˜ ๊ณ„์ •), ์‹ค์ œ Petra wallet ๊ณผ ์ƒํ˜ธ์ž‘์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ•ด๋‹น wallet์˜ privateKey, address๋ฅผ ๊ฐ€์ง€๊ณ  aptosAccount๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ˆ˜๋ น์ž์˜ privateKey๋ฅผ ์•Œ๊ณ  ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

new AptosClientํ•จ์ˆ˜์˜&nbsp; ํ˜ธ์ถœ ํ˜•์‹

offerToken๊นŒ์ง€๋งŒ ํ•˜๋ฉด ์ˆ˜๋ น์ž์˜ petra wallet์—์„œ ์ˆ˜๋ นํ•  ์ง€ ๊ฑฐ์ ˆํ•  ์ง€ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์„œ๋น„์Šค ์ƒ์—์„œ๋Š” offerToken๊นŒ์ง€๋งŒ ํ•˜๊ณ  claim์—ฌ๋ถ€๋Š” petra wallet์—์„œ ๊ฒฐ์ •ํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ณด์ž…๋‹ˆ๋‹ค.

๋Œ“๊ธ€