Skip to content

Commit a853447

Browse files
committed
Merge remote-tracking branch 'origin/feature/NFTPAR-373_chain_extensions' into feature/NFTPAR-366_upstream_updates
Signed-off-by: Yaroslav Bolyukin <iam@lach.pw>
2 parents e2828aa + 6df0cda commit a853447

8 files changed

Lines changed: 610 additions & 23 deletions

File tree

.devcontainer/Dockerfile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ FROM mcr.microsoft.com/vscode/devcontainers/rust:0-1
33
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive && \
44
apt-get -y install --no-install-recommends libssl-dev pkg-config libclang-dev clang
55

6+
RUN curl -L -o- https://github.com/WebAssembly/binaryen/releases/download/version_101/binaryen-version_101-x86_64-linux.tar.gz | \
7+
tar xz --strip-components=1 -C /usr
8+
69
USER vscode
710

811
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash && \
@@ -14,4 +17,4 @@ RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | b
1417
rustup default nightly-2021-05-30 && \
1518
rustup target add wasm32-unknown-unknown && \
1619
rustup component add rustfmt clippy && \
17-
cargo install cargo-expand cargo-edit
20+
cargo install cargo-expand cargo-edit cargo-contract

runtime/src/chain_extension.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ impl<C: Config> ChainExtension<C> for NFTExtension {
9797
let collection = pallet_nft::Module::<C>::get_collection(input.collection_id)?;
9898

9999
pallet_nft::Module::<C>::transfer_internal(
100-
&C::CrossAccountId::from_sub(env.ext().caller().clone()),
100+
&C::CrossAccountId::from_sub(env.ext().address().clone()),
101101
&C::CrossAccountId::from_sub(input.recipient),
102102
&collection,
103103
input.token_id,

smart_contracs/transfer/Cargo.toml

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@ version = "0.1.0"
44
authors = ["[Greg Zaitsev] <[your_email]>"]
55
edition = "2018"
66

7+
[workspace]
8+
79
[dependencies]
8-
ink_primitives = { git = "https://github.com/usetech-llc/ink", branch = "unique", default-features = false }
9-
ink_metadata = { git = "https://github.com/usetech-llc/ink", branch = "unique", default-features = false, features = ["derive"], optional = true }
10-
ink_env = { git = "https://github.com/usetech-llc/ink", branch = "unique", default-features = false }
11-
ink_storage = { git = "https://github.com/usetech-llc/ink", branch = "unique", default-features = false }
12-
ink_lang = { git = "https://github.com/usetech-llc/ink", branch = "unique", default-features = false }
10+
ink_primitives = { default-features = false }
11+
ink_metadata = { default-features = false, features = ["derive"], optional = true }
12+
ink_env = { default-features = false }
13+
ink_storage = { default-features = false }
14+
ink_lang = { default-features = false }
1315

14-
scale = { package = "parity-scale-codec", version = "1.3", default-features = false, features = ["derive"] }
15-
scale-info = { version = "0.4.1", default-features = false, features = ["derive"], optional = true }
16+
scale = { package = "parity-scale-codec", version = "2.1.1", default-features = false, features = ["derive"] }
17+
scale-info = { version = "0.6.0", default-features = false, features = ["derive"] }
1618

1719
[lib]
1820
name = "nft_transfer"
@@ -28,6 +30,7 @@ std = [
2830
"ink_metadata/std",
2931
"ink_env/std",
3032
"ink_storage/std",
33+
"ink_lang/std",
3134
"ink_primitives/std",
3235
"scale/std",
3336
"scale-info/std",

smart_contracs/transfer/lib.rs

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#![cfg_attr(not(feature = "std"), no_std)]
2+
extern crate alloc;
3+
use alloc::vec::Vec;
24

35
use ink_lang as ink;
46
use ink_env::{Environment, DefaultEnvironment};
@@ -36,18 +38,51 @@ impl ink_env::chain_extension::FromStatusCode for NftErrorCode {
3638
}
3739
}
3840

41+
#[derive(scale::Encode, scale::Decode, scale_info::TypeInfo)]
42+
pub enum CreateItemData {
43+
Nft {
44+
const_data: Vec<u8>,
45+
variable_data: Vec<u8>,
46+
},
47+
Fungible {
48+
value: u128,
49+
},
50+
ReFungible {
51+
const_data: Vec<u8>,
52+
variable_data: Vec<u8>,
53+
pieces: u128,
54+
},
55+
}
56+
57+
type DefaultAccountId = <DefaultEnvironment as Environment>::AccountId;
58+
3959
#[ink::chain_extension]
4060
pub trait NftChainExtension {
4161
type ErrorCode = NftErrorCode;
4262

4363
/// Transfer one NFT token from sender
4464
///
4565
#[ink(extension = 0, returns_result = false)]
46-
fn transfer(recipient: <DefaultEnvironment as Environment>::AccountId, collection_id: u32, token_id: u32, amount: u128);
66+
fn transfer(recipient: DefaultAccountId, collection_id: u32, token_id: u32, amount: u128);
67+
#[ink(extension = 1, returns_result = false)]
68+
fn create_item(owner: DefaultAccountId, collection_id: u32, data: CreateItemData);
69+
#[ink(extension = 2, returns_result = false)]
70+
fn create_multiple_items(owner: DefaultAccountId, collection_id: u32, data: Vec<CreateItemData>);
71+
#[ink(extension = 3, returns_result = false)]
72+
fn approve(spender: DefaultAccountId, collection_id: u32, item_id: u32, amount: u128);
73+
#[ink(extension = 4, returns_result = false)]
74+
fn transfer_from(owner: DefaultAccountId, recipient: DefaultAccountId, collection_id: u32, item_id: u32, amount: u128);
75+
#[ink(extension = 5, returns_result = false)]
76+
fn set_variable_meta_data(collection_id: u32, item_id: u32, data: Vec<u8>);
77+
#[ink(extension = 6, returns_result = false)]
78+
fn toggle_white_list(collection_id: u32, address: DefaultAccountId, whitelisted: bool);
4779
}
4880

49-
#[ink::contract(env = crate::NftEnvironment)]
81+
#[ink::contract(env = crate::NftEnvironment, dynamic_storage_allocator = true)]
5082
mod nft_transfer {
83+
use alloc::vec::Vec;
84+
// use ink_storage::Vec;
85+
use crate::CreateItemData;
5186

5287
#[ink(storage)]
5388
pub struct NftTransfer {
@@ -69,6 +104,42 @@ mod nft_transfer {
69104
.extension()
70105
.transfer(recipient, collection_id, token_id, amount);
71106
}
107+
#[ink(message)]
108+
pub fn create_item(&mut self, recipient: AccountId, collection_id: u32, data: CreateItemData) {
109+
let _ = self.env()
110+
.extension()
111+
.create_item(recipient, collection_id, data);
112+
}
113+
#[ink(message)]
114+
pub fn create_multiple_items(&mut self, owner: AccountId, collection_id: u32, data: Vec<CreateItemData>) {
115+
let _ = self.env()
116+
.extension()
117+
.create_multiple_items(owner, collection_id, data);
118+
}
119+
#[ink(message)]
120+
pub fn approve(&mut self, spender: AccountId, collection_id: u32, item_id: u32, amount: u128) {
121+
let _ = self.env()
122+
.extension()
123+
.approve(spender, collection_id, item_id, amount);
124+
}
125+
#[ink(message)]
126+
pub fn transfer_from(&mut self, owner: AccountId, recipient: AccountId, collection_id: u32, item_id: u32, amount: u128) {
127+
let _ = self.env()
128+
.extension()
129+
.transfer_from(owner, recipient, collection_id, item_id, amount);
130+
}
131+
#[ink(message)]
132+
pub fn set_variable_meta_data(&mut self, collection_id: u32, item_id: u32, data: Vec<u8>) {
133+
let _ = self.env()
134+
.extension()
135+
.set_variable_meta_data(collection_id, item_id, data);
136+
}
137+
#[ink(message)]
138+
pub fn toggle_white_list(&mut self, collection_id: u32, address: AccountId, whitelisted: bool) {
139+
let _ = self.env()
140+
.extension()
141+
.toggle_white_list(collection_id, address, whitelisted);
142+
}
72143

73144
}
74145

tests/src/contracts.test.ts

Lines changed: 175 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,24 @@ import {
1616
} from "./util/contracthelpers";
1717

1818
import {
19+
addToWhiteListExpectSuccess,
20+
approveExpectSuccess,
1921
createCollectionExpectSuccess,
2022
createItemExpectSuccess,
23+
enablePublicMintingExpectSuccess,
24+
enableWhiteListExpectSuccess,
2125
getGenericResult,
22-
normalizeAccountId
26+
normalizeAccountId,
27+
isWhitelisted,
28+
transferFromExpectSuccess
2329
} from "./util/helpers";
2430

2531

2632
chai.use(chaiAsPromised);
2733
const expect = chai.expect;
2834

2935
const value = 0;
30-
const gasLimit = 3000n * 1000000n;
36+
const gasLimit = 9000n * 1000000n;
3137
const marketContractAddress = '5CYN9j3YvRkqxewoxeSvRbhAym4465C57uMmX5j4yz99L5H6';
3238

3339
describe('Contracts', () => {
@@ -54,8 +60,10 @@ describe('Contracts', () => {
5460
expect(newContractInstance.address.toString()).to.equal(marketContractAddress);
5561
});
5662
});
63+
});
5764

58-
it('Can transfer NFT using smart contract.', async () => {
65+
describe.only('Chain extensions', () => {
66+
it('Transfer CE', async () => {
5967
await usingApi(async api => {
6068
const alice = privateKey("//Alice");
6169
const bob = privateKey("//Bob");
@@ -64,6 +72,9 @@ describe('Contracts', () => {
6472
const collectionId = await createCollectionExpectSuccess();
6573
const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');
6674
const [contract, deployer] = await deployTransferContract(api);
75+
const changeAdminTx = api.tx.nft.addCollectionAdmin(collectionId, contract.address);
76+
await submitTransactionAsync(alice, changeAdminTx);
77+
6778
const tokenBefore: any = (await api.query.nft.nftItemList(collectionId, tokenId) as any).toJSON();
6879

6980
// Transfer
@@ -78,4 +89,165 @@ describe('Contracts', () => {
7889
expect(tokenAfter.Owner).to.be.deep.equal(normalizeAccountId(bob.address));
7990
});
8091
});
92+
93+
it('Mint CE', async () => {
94+
await usingApi(async api => {
95+
const alice = privateKey('//Alice');
96+
const bob = privateKey('//Bob');
97+
98+
const collectionId = await createCollectionExpectSuccess();
99+
const [contract, deployer] = await deployTransferContract(api);
100+
await enablePublicMintingExpectSuccess(alice, collectionId);
101+
await enableWhiteListExpectSuccess(alice, collectionId);
102+
await addToWhiteListExpectSuccess(alice, collectionId, contract.address);
103+
await addToWhiteListExpectSuccess(alice, collectionId, bob.address);
104+
105+
const transferTx = contract.tx.createItem(value, gasLimit, bob.address, collectionId, { Nft: {const_data: '0x010203', variable_data: '0x020304' }});
106+
const events = await submitTransactionAsync(alice, transferTx);
107+
const result = getGenericResult(events);
108+
expect(result.success).to.be.true;
109+
110+
const tokensAfter: any = (await api.query.nft.nftItemList.entries(collectionId) as any).map((kv: any) => kv[1].toJSON());
111+
expect(tokensAfter).to.be.deep.equal([
112+
{
113+
Owner: bob.address,
114+
ConstData: '0x010203',
115+
VariableData: '0x020304',
116+
},
117+
]);
118+
});
119+
});
120+
121+
it('Bulk mint CE', async () => {
122+
await usingApi(async api => {
123+
const alice = privateKey('//Alice');
124+
const bob = privateKey('//Bob');
125+
126+
const collectionId = await createCollectionExpectSuccess();
127+
const [contract, deployer] = await deployTransferContract(api);
128+
await enablePublicMintingExpectSuccess(alice, collectionId);
129+
await enableWhiteListExpectSuccess(alice, collectionId);
130+
await addToWhiteListExpectSuccess(alice, collectionId, contract.address);
131+
await addToWhiteListExpectSuccess(alice, collectionId, bob.address);
132+
133+
const transferTx = contract.tx.createMultipleItems(value, gasLimit, bob.address, collectionId, [
134+
{ Nft: { const_data: '0x010203', variable_data: '0x020304' } },
135+
{ Nft: { const_data: '0x010204', variable_data: '0x020305' } },
136+
{ Nft: { const_data: '0x010205', variable_data: '0x020306' } }
137+
]);
138+
const events = await submitTransactionAsync(alice, transferTx);
139+
const result = getGenericResult(events);
140+
expect(result.success).to.be.true;
141+
142+
const tokensAfter: any = (await api.query.nft.nftItemList.entries(collectionId) as any)
143+
.map((kv: any) => kv[1].toJSON())
144+
.sort((a: any, b: any) => a.ConstData.localeCompare(b.ConstData));
145+
expect(tokensAfter).to.be.deep.equal([
146+
{
147+
Owner: bob.address,
148+
ConstData: '0x010203',
149+
VariableData: '0x020304',
150+
},
151+
{
152+
Owner: bob.address,
153+
ConstData: '0x010204',
154+
VariableData: '0x020305',
155+
},
156+
{
157+
Owner: bob.address,
158+
ConstData: '0x010205',
159+
VariableData: '0x020306',
160+
},
161+
]);
162+
});
163+
});
164+
165+
it('Approve CE', async () => {
166+
await usingApi(async api => {
167+
const alice = privateKey('//Alice');
168+
const bob = privateKey('//Bob');
169+
const charlie = privateKey('//Charlie');
170+
171+
const collectionId = await createCollectionExpectSuccess();
172+
const [contract, deployer] = await deployTransferContract(api);
173+
const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', contract.address.toString());
174+
175+
const transferTx = contract.tx.approve(value, gasLimit, bob.address, collectionId, tokenId, 1);
176+
const events = await submitTransactionAsync(alice, transferTx);
177+
const result = getGenericResult(events);
178+
expect(result.success).to.be.true;
179+
180+
await transferFromExpectSuccess(collectionId, tokenId, bob, contract.address.toString(), charlie, 1, 'NFT');
181+
});
182+
});
183+
184+
it('TransferFrom CE', async () => {
185+
await usingApi(async api => {
186+
const alice = privateKey('//Alice');
187+
const bob = privateKey('//Bob');
188+
const charlie = privateKey('//Charlie');
189+
190+
const collectionId = await createCollectionExpectSuccess();
191+
const [contract, deployer] = await deployTransferContract(api);
192+
const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);
193+
await approveExpectSuccess(collectionId, tokenId, bob, contract.address.toString(), 1);
194+
195+
const transferTx = contract.tx.transferFrom(value, gasLimit, bob.address, charlie.address, collectionId, tokenId, 1);
196+
const events = await submitTransactionAsync(alice, transferTx);
197+
const result = getGenericResult(events);
198+
expect(result.success).to.be.true;
199+
200+
const token: any = (await api.query.nft.nftItemList(collectionId, tokenId) as any).unwrap()
201+
expect(token.Owner.toString()).to.be.equal(charlie.address);
202+
});
203+
});
204+
205+
it('SetVariableMetaData CE', async () => {
206+
await usingApi(async api => {
207+
const alice = privateKey('//Alice');
208+
209+
const collectionId = await createCollectionExpectSuccess();
210+
const [contract, deployer] = await deployTransferContract(api);
211+
const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', contract.address.toString());
212+
213+
const transferTx = contract.tx.setVariableMetaData(value, gasLimit, collectionId, tokenId, '0x121314');
214+
const events = await submitTransactionAsync(alice, transferTx);
215+
const result = getGenericResult(events);
216+
expect(result.success).to.be.true;
217+
218+
const token: any = (await api.query.nft.nftItemList(collectionId, tokenId) as any).unwrap()
219+
expect(token.VariableData.toString()).to.be.equal('0x121314');
220+
});
221+
});
222+
223+
it('ToggleWhiteList CE', async () => {
224+
await usingApi(async api => {
225+
const alice = privateKey('//Alice');
226+
const bob = privateKey('//Bob');
227+
228+
const collectionId = await createCollectionExpectSuccess();
229+
const [contract, deployer] = await deployTransferContract(api);
230+
const changeAdminTx = api.tx.nft.addCollectionAdmin(collectionId, contract.address);
231+
await submitTransactionAsync(alice, changeAdminTx);
232+
233+
expect(await isWhitelisted(collectionId, bob.address)).to.be.false;
234+
235+
{
236+
const transferTx = contract.tx.toggleWhiteList(value, gasLimit, collectionId, bob.address, true);
237+
const events = await submitTransactionAsync(alice, transferTx);
238+
const result = getGenericResult(events);
239+
expect(result.success).to.be.true;
240+
241+
expect(await isWhitelisted(collectionId, bob.address)).to.be.true;
242+
}
243+
{
244+
const transferTx = contract.tx.toggleWhiteList(value, gasLimit, collectionId, bob.address, false);
245+
const events = await submitTransactionAsync(alice, transferTx);
246+
const result = getGenericResult(events);
247+
expect(result.success).to.be.true;
248+
249+
expect(await isWhitelisted(collectionId, bob.address)).to.be.false;
250+
}
251+
});
252+
});
81253
});

0 commit comments

Comments
 (0)