본문으로 건너뛰기

로열티

이 튜토리얼에서는 NFT(Non-Fungible Token) 스마트 컨트랙트를 계속 구축하고, NFT에 영구 로열티를 구현하는 방법을 배웁니다. 이를 통해 사람들은 NFT가 판매될 때 구매 가격의 일정 비율을 얻을 수 있습니다.

소개

지금쯤이면 로열티 지원을 제외하고는 완전한 NFT 컨트랙트가 있어야 합니다. To get started, go to the nft-contract-approval/ folder from our GitHub repository, or continue your work from the previous tutorials.

cd nft-contract-approval/
If you wish to see the finished code for this Royalty tutorial, you can find it in the nft-contract-royalty folder. :::

문제에 대한 생각

로열티 기능을 구현하려면, 먼저 NFT 판매 방식을 이해해야 합니다. 이전 튜토리얼에서는, NFT를 가진 사람이 적절하게 디코딩할 수 있는 메시지를 전달하여 nft_approve 함수를 통해 마켓플레이스에 NFT를 리스팅하는 것을 보았습니다. 사용자가 마켓플레이스에서 NFT를 구매하면 어떤 일이 일어나나요?

지금 가지고 있는 지식을 사용했을 때의 합리적인 결론은, 마켓플레이스가 교차 컨트랙트 호출을 수행하여 NFT를 구매자에게 전송하고, NFT 컨트랙트의 nft_transfer 메서드를 호출하는 것입니다. 해당 함수가, 완료되면 마켓플레이스는 구매자가 지불한 정확한 금액을 판매자에게 지불합니다.

이제 판매자가 아닌 다른 계정으로 가는 금액을 삭감할 수 있도록 확장할 수 있는 방법에 대해 생각해 보겠습니다.


현재 솔루션 확장

Since perpetual royalties will be on a per-token basis, it's safe to assume that you should be changing the Token and JsonToken structs. You need some way of keeping track of what percentage each account with a royalty should have. If you introduce a map of an account to an integer, that should do the trick.

Now, you need some way to relay that information to the marketplace. This method should be able to transfer the NFT exactly like the old solution but with the added benefit of telling the marketplace exactly what accounts should be paid what amounts. If you implement a method that transfers the NFT and then calculates exactly what accounts get paid and to what amount based on a passed-in balance, that should work nicely.

This is what the royalty standards outlined. Let's now move on and modify our smart contract to introduce this behavior.


컨트랙트 수정

The first thing you'll want to do is add the royalty information to the structs. Open the nft-contract-approval/src/metadata.rs file and add royalty to the Token struct:

pub royalty: HashMap<AccountId, u32>,

Second, you'll want to add royalty to the JsonToken struct as well:

pub royalty: HashMap<AccountId, u32>,

내부 헬퍼 함수

royalty_to_payout

To simplify the payout calculation, let's add a helper royalty_to_payout function to src/internal.rs. This will convert a percentage to the actual amount that should be paid. In order to allow for percentages less than 1%, you can give 100% a value of 10,000. This means that the minimum percentage you can give out is 0.01%, or 1. For example, if you wanted the account benji.testnet to have a perpetual royalty of 20%, you would insert the pair "benji.testnet": 2000 into the payout map.

If you were to use the royalty_to_payout function and pass in 2000 as the royalty_percentage and an amount_to_pay of 1 NEAR, it would return a value of 0.2 NEAR.


로열티

nft_payout

Let's now implement a method to check what accounts will be paid out for an NFT given an amount, or balance. Open the nft-contract/src/royalty.rs file, and modify the nft_payout function as shown.

This function will loop through the token's royalty map and take the balance and convert that to a payout using the royalty_to_payout function you created earlier. It will give the owner of the token whatever is left from the total royalties. As an example:

You have a token with the following royalty field:

Token {
owner_id: "damian",
royalty: {
"benji": 1000,
"josh": 500,
"mike": 2000
}
}

If a user were to call nft_payout on the token and pass in a balance of 1 NEAR, it would loop through the token's royalty field and insert the following into the payout object:

Payout {
payout: {
"benji": 0.1 NEAR,
"josh": 0.05 NEAR,
"mike": 0.2 NEAR
}
}

At the very end, it will insert damian into the payout object and give him 1 NEAR - 0.1 - 0.05 - 0.2 = 0.65 NEAR.

nft_transfer_payout

Now that you know how payouts are calculated, it's time to create the function that will transfer the NFT and return the payout to the marketplace.


영구 로열티

To add support for perpetual royalties, let's edit the src/mint.rs file. First, add an optional parameter for perpetual royalties. This is what will determine what percentage goes to which accounts when the NFT is purchased. You will also need to create and insert the royalty to be put in the Token object:

Next, you can use the CLI to query the new nft_payout function and validate that it works correctly.

구조체 구현에 로열티 객체 추가

Since you've added a new field to your Token and JsonToken structs, you need to edit your implementations accordingly. Move to the nft-contract/src/internal.rs file and edit the part of your internal_transfer function that creates the new Token object:

Once that's finished, move to the nft-contract-approval/src/nft_core.rs file. You need to edit your implementation of nft_token so that the JsonToken sends back the new royalty information.


컨트랙트 배포

As you saw in the previous tutorial, adding changes like these will cause problems when redeploying. Since these changes affect all the other tokens and the state won't be able to automatically be inherited by the new code, simply redeploying the contract will lead to errors. For this reason, you'll create a new account again.

Deployment and initialization

Next, you'll deploy this contract to the network.

export ROYALTY_NFT_CONTRACT_ID=<accountId>
near account create-account sponsor-by-faucet-service $ROYALTY_NFT_CONTRACT_ID autogenerate-new-keypair save-to-legacy-keychain network-config testnet create

Using the cargo-near, deploy and initialize the contract as you did in the previous tutorials:

cargo near deploy $ROYALTY_NFT_CONTRACT_ID with-init-call new_default_meta json-args '{"owner_id": "'$ROYALTY_NFT_CONTRACT_ID'"}' prepaid-gas '100.0 Tgas' attached-deposit '0 NEAR' network-config testnet sign-with-keychain send

Minting

다음으로 토큰을 발행해야 합니다. 이 명령을 실행하면 토큰 ID "royalty-token"로 토큰이 발행되고, 수신자가 새 계정이 됩니다. 또한 토큰이 판매될 때마다 영구 로열티를 받는 두 개의 계정을 포함한 맵을 전달합니다.

near contract call-function as-transaction $ROYALTY_NFT_CONTRACT_ID nft_mint json-args '{"token_id": "royalty-token", "metadata": {"title": "Royalty Token", "description": "testing out the new royalty extension of the standard", "media": "https://bafybeiftczwrtyr3k7a2k4vutd3amkwsmaqyhrdzlhvpt33dyjivufqusq.ipfs.dweb.link/goteam-gif.gif"}, "receiver_id": "'$ROYALTY_NFT_CONTRACT_ID'", "perpetual_royalties": {"benjiman.testnet": 2000, "mike.testnet": 1000, "josh.testnet": 500}}' prepaid-gas '100.0 Tgas' attached-deposit '0.1 NEAR' sign-as $ROYALTY_NFT_CONTRACT_ID network-config testnet sign-with-legacy-keychain send

열거(Enumeration) 함수 중 하나를 호출하여 모든 것이 제대로 진행되었는지 확인할 수 있습니다.

near contract call-function as-read-only $ROYALTY_NFT_CONTRACT_ID nft_tokens_for_owner json-args '{"account_id": "'$ROYALTY_NFT_CONTRACT_ID'", "limit": 10}' network-config testnet now

그러면 다음과 유사한 출력이 반환됩니다.

[
{
"token_id": "royalty-token",
"owner_id": "royalty.goteam.examples.testnet",
"metadata": {
"title": "Royalty Token",
"description": "testing out the new royalty extension of the standard",
"media": "https://bafybeiftczwrtyr3k7a2k4vutd3amkwsmaqyhrdzlhvpt33dyjivufqusq.ipfs.dweb.link/goteam-gif.gif",
"media_hash": null,
"copies": null,
"issued_at": null,
"expires_at": null,
"starts_at": null,
"updated_at": null,
"extra": null,
"reference": null,
"reference_hash": null
},
"approved_account_ids": {},
"royalty": {
"josh.testnet": 500,
"benjiman.testnet": 2000,
"mike.testnet": 1000
}
}
]

이제 이 NFT의 전체 매출의 35%를 합산한 3개의 계정을 포함하는 로열티 필드가 어떻게 생겼는지 확인해 보실까요? 작동하는 것 같습니다! 화이팅 :)

NFT 지급

Let's calculate the payout for the "royalty-token" NFT, given a balance of 100 yoctoNEAR. nft_payout 함수로 전달되는 금액이 yoctoNEAR 단위로 표시될 것으로 예상된다는 점에 유의하는 것이 중요합니다.

near contract call-function as-read-only $ROYALTY_NFT_CONTRACT_ID nft_payout json-args '{"token_id": "royalty-token", "balance": "100", "max_len_payout": 100}' network-config testnet now

이 명령은 다음과 유사한 출력을 반환해야 합니다.

{
payout: {
'josh.testnet': '5',
'royalty.goteam.examples.testnet': '65',
'mike.testnet': '10',
'benjiman.testnet': '20'
}
}

If the NFT was sold for 100 yoctoNEAR, josh would get 5, Benji would get 20, mike would get 10, and the owner, in this case royalty.goteam.examples.testnet would get the rest: 65.

결론

이 시점에서 마켓플레이스와 상호 작용하기 위해 완벽하게 작동하는 NFT 컨트랙트에 필요한 모든 것이 존재합니다. 구현할 수 있는 마지막 남은 표준은 이벤트 표준입니다. 이렇게 하면 인덱서는 호출되는 함수에 대해 알 수 있기에, 지갑의 수집품 탭을 채우는 데 사용할 수 있는 정보 등에 대해 추적하는 것을 더 쉽고 안정적으로 할 수 있게 됩니다.

remember If you want to see the finished code from this tutorial, you can go to the nft-contract-royalty folder. :::
문서 버전 관리

글을 작성하는 시점에서, 해당 예제는 다음 버전에서 작동합니다.

  • rustc: 1.77.1
  • near-cli-rs: 0.11.0
  • cargo-near 0.6.1
  • NFT standard: NEP171, version 1.0.0
  • 열거 표준: NEP181, 1.0.0 버전
  • 로열티 표준: NEP199, 2.0.0 버전
Was this page helpful?