diff --git a/Makefile.toml b/Makefile.toml index 706bcb1..8af380c 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -46,7 +46,7 @@ args = ["fmt", "--all", "--", "--check"] [tasks.did] description = "Generate did files" -run_task = [{ name = "sell_contract_did" }, { name = "dfx_generate" }] +dependencies = ["sell_contract_did", "dfx_generate"] workspace = false [tasks.sell_contract_did] diff --git a/integration-tests/tests/sell_contract.test.js b/integration-tests/tests/sell_contract.test.js index f32a545..809ed86 100644 --- a/integration-tests/tests/sell_contract.test.js +++ b/integration-tests/tests/sell_contract.test.js @@ -3,6 +3,7 @@ import { Principal } from "@dfinity/principal"; import { sellContract } from "./actor"; +/* test("should be able to update fly canister principal", async () => { const principal = Principal.fromText("rrkah-fqaaa-aaaaa-aaaaq-cai"); await sellContract.admin_set_fly_canister(principal); @@ -12,3 +13,9 @@ test("should be able to update marketplace canister principal", async () => { const principal = Principal.fromText("rrkah-fqaaa-aaaaa-aaaaq-cai"); await sellContract.admin_set_marketplace_canister(principal); }); +*/ + +test("should be able to get the total amount of nfts", async () => { + const totalSupply = await sellContract.total_supply(); + expect(totalSupply).toBeGreaterThanOrEqual(0); +}); diff --git a/src/declarations/sell_contract/sell_contract.did.d.ts b/src/declarations/sell_contract/sell_contract.did.d.ts index 5e06d12..8fef38d 100644 --- a/src/declarations/sell_contract/sell_contract.did.d.ts +++ b/src/declarations/sell_contract/sell_contract.did.d.ts @@ -15,8 +15,56 @@ export interface Contract { 'mfly_reward' : bigint, } export type FlyError = { 'StorageError' : null }; +export type GenericValue = { 'Nat64Content' : bigint } | + { 'Nat32Content' : number } | + { 'BoolContent' : boolean } | + { 'Nat8Content' : number } | + { 'Int64Content' : bigint } | + { 'IntContent' : bigint } | + { 'NatContent' : bigint } | + { 'Nat16Content' : number } | + { 'Int32Content' : number } | + { 'Int8Content' : number } | + { 'FloatContent' : number } | + { 'Int16Content' : number } | + { 'BlobContent' : Uint8Array | number[] } | + { 'NestedContent' : Vec } | + { 'Principal' : Principal } | + { 'TextContent' : string }; +export interface Metadata { + 'logo' : [] | [string], + 'name' : [] | [string], + 'created_at' : bigint, + 'upgraded_at' : bigint, + 'custodians' : Array, + 'symbol' : [] | [string], +} +export type NftError = { 'UnauthorizedOperator' : null } | + { 'SelfTransfer' : null } | + { 'TokenNotFound' : null } | + { 'UnauthorizedOwner' : null } | + { 'TxNotFound' : null } | + { 'SelfApprove' : null } | + { 'OperatorNotFound' : null } | + { 'ExistedNFT' : null } | + { 'OwnerNotFound' : null } | + { 'Other' : string }; export type Result = { 'Ok' : null } | { 'Err' : SellContractError }; +export type Result_1 = { 'Ok' : bigint } | + { 'Err' : NftError }; +export type Result_2 = { 'Ok' : boolean } | + { 'Err' : NftError }; +export type Result_3 = { 'Ok' : [] | [Principal] } | + { 'Err' : NftError }; +export type Result_4 = { 'Ok' : Array } | + { 'Err' : NftError }; +export type Result_5 = { 'Ok' : Array } | + { 'Err' : NftError }; +export type Result_6 = { 'Ok' : TokenMetadata } | + { 'Err' : NftError }; +export type Result_7 = { 'Ok' : TxEvent } | + { 'Err' : NftError }; export type SellContractError = { 'Fly' : FlyError } | { 'Configuration' : ConfigurationError } | { 'Unauthorized' : null } | @@ -27,6 +75,16 @@ export interface SellContractInitData { 'custodians' : Array, 'marketplace_canister' : Principal, } +export interface Stats { + 'cycles' : bigint, + 'total_transactions' : bigint, + 'total_unique_holders' : bigint, + 'total_supply' : bigint, +} +export type SupportedInterface = { 'Burn' : null } | + { 'Mint' : null } | + { 'Approval' : null } | + { 'TransactionHistory' : null }; export type TokenError = { 'ContractValueIsNotMultipleOfInstallments' : null } | { 'TokenAlreadyExists' : bigint } | { 'TokensMismatch' : null } | @@ -37,6 +95,48 @@ export type TokenError = { 'ContractValueIsNotMultipleOfInstallments' : null } | { 'TokenIsBurned' : bigint } | { 'InvalidExpirationDate' : null } | { 'BadMintTokenOwner' : bigint }; +export interface TokenMetadata { + 'transferred_at' : [] | [bigint], + 'transferred_by' : [] | [Principal], + 'owner' : [] | [Principal], + 'operator' : [] | [Principal], + 'approved_at' : [] | [bigint], + 'approved_by' : [] | [Principal], + 'properties' : Array<[string, GenericValue]>, + 'is_burned' : boolean, + 'token_identifier' : bigint, + 'burned_at' : [] | [bigint], + 'burned_by' : [] | [Principal], + 'minted_at' : bigint, + 'minted_by' : Principal, +} +export interface TxEvent { + 'time' : bigint, + 'operation' : string, + 'details' : Array<[string, GenericValue]>, + 'caller' : Principal, +} +export type Vec = Array< + [ + string, + { 'Nat64Content' : bigint } | + { 'Nat32Content' : number } | + { 'BoolContent' : boolean } | + { 'Nat8Content' : number } | + { 'Int64Content' : bigint } | + { 'IntContent' : bigint } | + { 'NatContent' : bigint } | + { 'Nat16Content' : number } | + { 'Int32Content' : number } | + { 'Int8Content' : number } | + { 'FloatContent' : number } | + { 'Int16Content' : number } | + { 'BlobContent' : Uint8Array | number[] } | + { 'NestedContent' : Vec } | + { 'Principal' : Principal } | + { 'TextContent' : string }, + ] +>; export interface _SERVICE { 'admin_register_contract' : ActorMethod< [bigint, Principal, Array, string, bigint, bigint, BuildingData], @@ -44,6 +144,40 @@ export interface _SERVICE { >, 'admin_set_fly_canister' : ActorMethod<[Principal], undefined>, 'admin_set_marketplace_canister' : ActorMethod<[Principal], undefined>, + 'approve' : ActorMethod<[Principal, bigint], Result_1>, + 'balance_of' : ActorMethod<[Principal], Result_1>, + 'burn' : ActorMethod<[bigint], Result_1>, + 'custodians' : ActorMethod<[], Array>, + 'cycles' : ActorMethod<[], bigint>, 'get_contract' : ActorMethod<[bigint], [] | [Contract]>, 'get_contracts' : ActorMethod<[], Array>, + 'is_approved_for_all' : ActorMethod<[Principal, Principal], Result_2>, + 'logo' : ActorMethod<[], [] | [string]>, + 'metadata' : ActorMethod<[], Metadata>, + 'mint' : ActorMethod< + [Principal, bigint, Array<[string, GenericValue]>], + Result_1 + >, + 'name' : ActorMethod<[], [] | [string]>, + 'operator_of' : ActorMethod<[bigint], Result_3>, + 'operator_token_identifiers' : ActorMethod<[Principal], Result_4>, + 'operator_token_metadata' : ActorMethod<[Principal], Result_5>, + 'owner_of' : ActorMethod<[bigint], Result_3>, + 'owner_token_identifiers' : ActorMethod<[Principal], Result_4>, + 'owner_token_metadata' : ActorMethod<[Principal], Result_5>, + 'set_approval_for_all' : ActorMethod<[Principal, boolean], Result_1>, + 'set_custodians' : ActorMethod<[Array], undefined>, + 'set_logo' : ActorMethod<[string], undefined>, + 'set_name' : ActorMethod<[string], undefined>, + 'set_symbol' : ActorMethod<[string], undefined>, + 'stats' : ActorMethod<[], Stats>, + 'supported_interfaces' : ActorMethod<[], Array>, + 'symbol' : ActorMethod<[], [] | [string]>, + 'token_metadata' : ActorMethod<[bigint], Result_6>, + 'total_supply' : ActorMethod<[], bigint>, + 'total_transactions' : ActorMethod<[], bigint>, + 'total_unique_holders' : ActorMethod<[], bigint>, + 'transaction' : ActorMethod<[bigint], Result_7>, + 'transfer' : ActorMethod<[Principal, bigint], Result_1>, + 'transfer_from' : ActorMethod<[Principal, Principal, bigint], Result_1>, } diff --git a/src/declarations/sell_contract/sell_contract.did.js b/src/declarations/sell_contract/sell_contract.did.js index 1dd498b..079974b 100644 --- a/src/declarations/sell_contract/sell_contract.did.js +++ b/src/declarations/sell_contract/sell_contract.did.js @@ -1,4 +1,5 @@ export const idlFactory = ({ IDL }) => { + const Vec = IDL.Rec(); const SellContractInitData = IDL.Record({ 'fly_canister' : IDL.Principal, 'custodians' : IDL.Vec(IDL.Principal), @@ -30,6 +31,19 @@ export const idlFactory = ({ IDL }) => { 'StorageError' : IDL.Null, }); const Result = IDL.Variant({ 'Ok' : IDL.Null, 'Err' : SellContractError }); + const NftError = IDL.Variant({ + 'UnauthorizedOperator' : IDL.Null, + 'SelfTransfer' : IDL.Null, + 'TokenNotFound' : IDL.Null, + 'UnauthorizedOwner' : IDL.Null, + 'TxNotFound' : IDL.Null, + 'SelfApprove' : IDL.Null, + 'OperatorNotFound' : IDL.Null, + 'ExistedNFT' : IDL.Null, + 'OwnerNotFound' : IDL.Null, + 'Other' : IDL.Text, + }); + const Result_1 = IDL.Variant({ 'Ok' : IDL.Nat, 'Err' : NftError }); const Contract = IDL.Record({ 'id' : IDL.Nat, 'value' : IDL.Nat64, @@ -40,6 +54,102 @@ export const idlFactory = ({ IDL }) => { 'buyers' : IDL.Vec(IDL.Principal), 'mfly_reward' : IDL.Nat64, }); + const Result_2 = IDL.Variant({ 'Ok' : IDL.Bool, 'Err' : NftError }); + const Metadata = IDL.Record({ + 'logo' : IDL.Opt(IDL.Text), + 'name' : IDL.Opt(IDL.Text), + 'created_at' : IDL.Nat64, + 'upgraded_at' : IDL.Nat64, + 'custodians' : IDL.Vec(IDL.Principal), + 'symbol' : IDL.Opt(IDL.Text), + }); + Vec.fill( + IDL.Vec( + IDL.Tuple( + IDL.Text, + IDL.Variant({ + 'Nat64Content' : IDL.Nat64, + 'Nat32Content' : IDL.Nat32, + 'BoolContent' : IDL.Bool, + 'Nat8Content' : IDL.Nat8, + 'Int64Content' : IDL.Int64, + 'IntContent' : IDL.Int, + 'NatContent' : IDL.Nat, + 'Nat16Content' : IDL.Nat16, + 'Int32Content' : IDL.Int32, + 'Int8Content' : IDL.Int8, + 'FloatContent' : IDL.Float64, + 'Int16Content' : IDL.Int16, + 'BlobContent' : IDL.Vec(IDL.Nat8), + 'NestedContent' : Vec, + 'Principal' : IDL.Principal, + 'TextContent' : IDL.Text, + }), + ) + ) + ); + const GenericValue = IDL.Variant({ + 'Nat64Content' : IDL.Nat64, + 'Nat32Content' : IDL.Nat32, + 'BoolContent' : IDL.Bool, + 'Nat8Content' : IDL.Nat8, + 'Int64Content' : IDL.Int64, + 'IntContent' : IDL.Int, + 'NatContent' : IDL.Nat, + 'Nat16Content' : IDL.Nat16, + 'Int32Content' : IDL.Int32, + 'Int8Content' : IDL.Int8, + 'FloatContent' : IDL.Float64, + 'Int16Content' : IDL.Int16, + 'BlobContent' : IDL.Vec(IDL.Nat8), + 'NestedContent' : Vec, + 'Principal' : IDL.Principal, + 'TextContent' : IDL.Text, + }); + const Result_3 = IDL.Variant({ + 'Ok' : IDL.Opt(IDL.Principal), + 'Err' : NftError, + }); + const Result_4 = IDL.Variant({ 'Ok' : IDL.Vec(IDL.Nat), 'Err' : NftError }); + const TokenMetadata = IDL.Record({ + 'transferred_at' : IDL.Opt(IDL.Nat64), + 'transferred_by' : IDL.Opt(IDL.Principal), + 'owner' : IDL.Opt(IDL.Principal), + 'operator' : IDL.Opt(IDL.Principal), + 'approved_at' : IDL.Opt(IDL.Nat64), + 'approved_by' : IDL.Opt(IDL.Principal), + 'properties' : IDL.Vec(IDL.Tuple(IDL.Text, GenericValue)), + 'is_burned' : IDL.Bool, + 'token_identifier' : IDL.Nat, + 'burned_at' : IDL.Opt(IDL.Nat64), + 'burned_by' : IDL.Opt(IDL.Principal), + 'minted_at' : IDL.Nat64, + 'minted_by' : IDL.Principal, + }); + const Result_5 = IDL.Variant({ + 'Ok' : IDL.Vec(TokenMetadata), + 'Err' : NftError, + }); + const Stats = IDL.Record({ + 'cycles' : IDL.Nat, + 'total_transactions' : IDL.Nat, + 'total_unique_holders' : IDL.Nat, + 'total_supply' : IDL.Nat, + }); + const SupportedInterface = IDL.Variant({ + 'Burn' : IDL.Null, + 'Mint' : IDL.Null, + 'Approval' : IDL.Null, + 'TransactionHistory' : IDL.Null, + }); + const Result_6 = IDL.Variant({ 'Ok' : TokenMetadata, 'Err' : NftError }); + const TxEvent = IDL.Record({ + 'time' : IDL.Nat64, + 'operation' : IDL.Text, + 'details' : IDL.Vec(IDL.Tuple(IDL.Text, GenericValue)), + 'caller' : IDL.Principal, + }); + const Result_7 = IDL.Variant({ 'Ok' : TxEvent, 'Err' : NftError }); return IDL.Service({ 'admin_register_contract' : IDL.Func( [ @@ -56,8 +166,71 @@ export const idlFactory = ({ IDL }) => { ), 'admin_set_fly_canister' : IDL.Func([IDL.Principal], [], []), 'admin_set_marketplace_canister' : IDL.Func([IDL.Principal], [], []), + 'approve' : IDL.Func([IDL.Principal, IDL.Nat], [Result_1], []), + 'balance_of' : IDL.Func([IDL.Principal], [Result_1], ['query']), + 'burn' : IDL.Func([IDL.Nat], [Result_1], []), + 'custodians' : IDL.Func([], [IDL.Vec(IDL.Principal)], ['query']), + 'cycles' : IDL.Func([], [IDL.Nat], ['query']), 'get_contract' : IDL.Func([IDL.Nat], [IDL.Opt(Contract)], ['query']), 'get_contracts' : IDL.Func([], [IDL.Vec(IDL.Nat)], ['query']), + 'is_approved_for_all' : IDL.Func( + [IDL.Principal, IDL.Principal], + [Result_2], + [], + ), + 'logo' : IDL.Func([], [IDL.Opt(IDL.Text)], ['query']), + 'metadata' : IDL.Func([], [Metadata], ['query']), + 'mint' : IDL.Func( + [IDL.Principal, IDL.Nat, IDL.Vec(IDL.Tuple(IDL.Text, GenericValue))], + [Result_1], + [], + ), + 'name' : IDL.Func([], [IDL.Opt(IDL.Text)], ['query']), + 'operator_of' : IDL.Func([IDL.Nat], [Result_3], ['query']), + 'operator_token_identifiers' : IDL.Func( + [IDL.Principal], + [Result_4], + ['query'], + ), + 'operator_token_metadata' : IDL.Func( + [IDL.Principal], + [Result_5], + ['query'], + ), + 'owner_of' : IDL.Func([IDL.Nat], [Result_3], ['query']), + 'owner_token_identifiers' : IDL.Func( + [IDL.Principal], + [Result_4], + ['query'], + ), + 'owner_token_metadata' : IDL.Func([IDL.Principal], [Result_5], ['query']), + 'set_approval_for_all' : IDL.Func( + [IDL.Principal, IDL.Bool], + [Result_1], + [], + ), + 'set_custodians' : IDL.Func([IDL.Vec(IDL.Principal)], [], []), + 'set_logo' : IDL.Func([IDL.Text], [], []), + 'set_name' : IDL.Func([IDL.Text], [], []), + 'set_symbol' : IDL.Func([IDL.Text], [], []), + 'stats' : IDL.Func([], [Stats], ['query']), + 'supported_interfaces' : IDL.Func( + [], + [IDL.Vec(SupportedInterface)], + ['query'], + ), + 'symbol' : IDL.Func([], [IDL.Opt(IDL.Text)], ['query']), + 'token_metadata' : IDL.Func([IDL.Nat], [Result_6], ['query']), + 'total_supply' : IDL.Func([], [IDL.Nat], ['query']), + 'total_transactions' : IDL.Func([], [IDL.Nat], ['query']), + 'total_unique_holders' : IDL.Func([], [IDL.Nat], ['query']), + 'transaction' : IDL.Func([IDL.Nat], [Result_7], ['query']), + 'transfer' : IDL.Func([IDL.Principal, IDL.Nat], [Result_1], []), + 'transfer_from' : IDL.Func( + [IDL.Principal, IDL.Principal, IDL.Nat], + [Result_1], + [], + ), }); }; export const init = ({ IDL }) => { diff --git a/src/dip721/src/lib.rs b/src/dip721/src/lib.rs index db30914..432483f 100644 --- a/src/dip721/src/lib.rs +++ b/src/dip721/src/lib.rs @@ -106,6 +106,10 @@ pub trait Dip721 { /// Interface: approval fn is_approved_for_all(owner: Principal, operator: Principal) -> Result; + /// Sends the callers nft token_identifier to `to`` and returns a nat that represents a + /// transaction id that can be used at the transaction method. + async fn transfer(to: Principal, token_identifier: TokenIdentifier) -> Result; + /// Caller of this method is able to transfer the NFT token_identifier that is in from's balance to to's balance if the caller is an approved operator to do so. /// /// If the transfer goes through, returns a nat that represents the CAP History transaction ID that can be used at the transaction method. diff --git a/src/sell_contract/sell_contract.did b/src/sell_contract/sell_contract.did index 9debf8c..2bc5c54 100644 --- a/src/sell_contract/sell_contract.did +++ b/src/sell_contract/sell_contract.did @@ -11,7 +11,52 @@ type Contract = record { mfly_reward : nat64; }; type FlyError = variant { StorageError }; +type GenericValue = variant { + Nat64Content : nat64; + Nat32Content : nat32; + BoolContent : bool; + Nat8Content : nat8; + Int64Content : int64; + IntContent : int; + NatContent : nat; + Nat16Content : nat16; + Int32Content : int32; + Int8Content : int8; + FloatContent : float64; + Int16Content : int16; + BlobContent : vec nat8; + NestedContent : Vec; + Principal : principal; + TextContent : text; +}; +type Metadata = record { + logo : opt text; + name : opt text; + created_at : nat64; + upgraded_at : nat64; + custodians : vec principal; + symbol : opt text; +}; +type NftError = variant { + UnauthorizedOperator; + SelfTransfer; + TokenNotFound; + UnauthorizedOwner; + TxNotFound; + SelfApprove; + OperatorNotFound; + ExistedNFT; + OwnerNotFound; + Other : text; +}; type Result = variant { Ok; Err : SellContractError }; +type Result_1 = variant { Ok : nat; Err : NftError }; +type Result_2 = variant { Ok : bool; Err : NftError }; +type Result_3 = variant { Ok : opt principal; Err : NftError }; +type Result_4 = variant { Ok : vec nat; Err : NftError }; +type Result_5 = variant { Ok : vec TokenMetadata; Err : NftError }; +type Result_6 = variant { Ok : TokenMetadata; Err : NftError }; +type Result_7 = variant { Ok : TxEvent; Err : NftError }; type SellContractError = variant { Fly : FlyError; Configuration : ConfigurationError; @@ -24,6 +69,13 @@ type SellContractInitData = record { custodians : vec principal; marketplace_canister : principal; }; +type Stats = record { + cycles : nat; + total_transactions : nat; + total_unique_holders : nat; + total_supply : nat; +}; +type SupportedInterface = variant { Burn; Mint; Approval; TransactionHistory }; type TokenError = variant { ContractValueIsNotMultipleOfInstallments; TokenAlreadyExists : nat; @@ -36,6 +88,48 @@ type TokenError = variant { InvalidExpirationDate; BadMintTokenOwner : nat; }; +type TokenMetadata = record { + transferred_at : opt nat64; + transferred_by : opt principal; + owner : opt principal; + operator : opt principal; + approved_at : opt nat64; + approved_by : opt principal; + properties : vec record { text; GenericValue }; + is_burned : bool; + token_identifier : nat; + burned_at : opt nat64; + burned_by : opt principal; + minted_at : nat64; + minted_by : principal; +}; +type TxEvent = record { + time : nat64; + operation : text; + details : vec record { text; GenericValue }; + caller : principal; +}; +type Vec = vec record { + text; + variant { + Nat64Content : nat64; + Nat32Content : nat32; + BoolContent : bool; + Nat8Content : nat8; + Int64Content : int64; + IntContent : int; + NatContent : nat; + Nat16Content : nat16; + Int32Content : int32; + Int8Content : int8; + FloatContent : float64; + Int16Content : int16; + BlobContent : vec nat8; + NestedContent : Vec; + Principal : principal; + TextContent : text; + }; +}; service : (SellContractInitData) -> { admin_register_contract : ( nat, @@ -48,6 +142,37 @@ service : (SellContractInitData) -> { ) -> (Result); admin_set_fly_canister : (principal) -> (); admin_set_marketplace_canister : (principal) -> (); + approve : (principal, nat) -> (Result_1); + balance_of : (principal) -> (Result_1) query; + burn : (nat) -> (Result_1); + custodians : () -> (vec principal) query; + cycles : () -> (nat) query; get_contract : (nat) -> (opt Contract) query; get_contracts : () -> (vec nat) query; + is_approved_for_all : (principal, principal) -> (Result_2); + logo : () -> (opt text) query; + metadata : () -> (Metadata) query; + mint : (principal, nat, vec record { text; GenericValue }) -> (Result_1); + name : () -> (opt text) query; + operator_of : (nat) -> (Result_3) query; + operator_token_identifiers : (principal) -> (Result_4) query; + operator_token_metadata : (principal) -> (Result_5) query; + owner_of : (nat) -> (Result_3) query; + owner_token_identifiers : (principal) -> (Result_4) query; + owner_token_metadata : (principal) -> (Result_5) query; + set_approval_for_all : (principal, bool) -> (Result_1); + set_custodians : (vec principal) -> (); + set_logo : (text) -> (); + set_name : (text) -> (); + set_symbol : (text) -> (); + stats : () -> (Stats) query; + supported_interfaces : () -> (vec SupportedInterface) query; + symbol : () -> (opt text) query; + token_metadata : (nat) -> (Result_6) query; + total_supply : () -> (nat) query; + total_transactions : () -> (nat) query; + total_unique_holders : () -> (nat) query; + transaction : (nat) -> (Result_7) query; + transfer : (principal, nat) -> (Result_1); + transfer_from : (principal, principal, nat) -> (Result_1); } \ No newline at end of file diff --git a/src/sell_contract/src/app.rs b/src/sell_contract/src/app.rs index f7e408f..8c0f4cf 100644 --- a/src/sell_contract/src/app.rs +++ b/src/sell_contract/src/app.rs @@ -436,6 +436,12 @@ impl Dip721 for SellContract { Err(NftError::Other("Not implemented".to_string())) } + /// Sends the callers nft token_identifier to `to`` and returns a nat that represents a + /// transaction id that can be used at the transaction method. + async fn transfer(to: Principal, token_identifier: TokenIdentifier) -> Result { + Self::transfer_from(caller(), to, token_identifier).await + } + /// Caller of this method is able to transfer the NFT token_identifier that is in from's balance to to's balance /// if the caller is an approved operator to do so. /// @@ -452,8 +458,12 @@ impl Dip721 for SellContract { Some(contract) => contract, None => return Err(NftError::TokenNotFound), }; + // verify that from owner is the same as the token's + if token.owner != Some(owner) { + return Err(NftError::OwnerNotFound); + } // verify that owner is not the same as to - if owner == to { + if token.owner == Some(to) { return Err(NftError::SelfTransfer); } diff --git a/src/sell_contract/src/lib.rs b/src/sell_contract/src/lib.rs index 8bd515b..3ddcbc6 100644 --- a/src/sell_contract/src/lib.rs +++ b/src/sell_contract/src/lib.rs @@ -1,6 +1,7 @@ -use candid::{candid_method, Principal}; +use candid::{candid_method, Nat, Principal}; use did::sell_contract::{BuildingData, Contract, SellContractInitData, SellContractResult}; use did::ID; +use dip721::Dip721 as _; use ic_cdk_macros::{init, post_upgrade, query, update}; mod app; @@ -72,6 +73,228 @@ pub fn admin_set_marketplace_canister(canister_id: Principal) { // DIP721 +#[query] +#[candid_method(query)] +pub fn metadata() -> dip721::Metadata { + SellContract::metadata() +} + +#[query] +#[candid_method(query)] +pub fn stats() -> dip721::Stats { + SellContract::stats() +} + +#[query] +#[candid_method(query)] +pub fn logo() -> Option { + SellContract::logo() +} + +#[update] +#[candid_method(update)] +pub fn set_logo(logo: String) { + SellContract::set_logo(logo) +} + +#[query] +#[candid_method(query)] +pub fn name() -> Option { + SellContract::name() +} + +#[update] +#[candid_method(update)] +pub fn set_name(name: String) { + SellContract::set_name(name) +} + +#[query] +#[candid_method(query)] +pub fn symbol() -> Option { + SellContract::symbol() +} + +#[update] +#[candid_method(update)] +pub fn set_symbol(symbol: String) { + SellContract::set_symbol(symbol) +} + +#[query] +#[candid_method(query)] +pub fn custodians() -> Vec { + SellContract::custodians() +} + +#[update] +#[candid_method(update)] +pub fn set_custodians(custodians: Vec) { + SellContract::set_custodians(custodians) +} + +#[query] +#[candid_method(query)] +pub fn cycles() -> Nat { + SellContract::cycles() +} + +#[query] +#[candid_method(query)] +pub fn total_unique_holders() -> Nat { + SellContract::total_unique_holders() +} + +#[query] +#[candid_method(query)] +pub fn token_metadata( + token_identifier: dip721::TokenIdentifier, +) -> Result { + SellContract::token_metadata(token_identifier) +} + +#[query] +#[candid_method(query)] +pub fn balance_of(owner: Principal) -> Result { + SellContract::balance_of(owner) +} + +#[query] +#[candid_method(query)] +pub fn owner_of( + token_identifier: dip721::TokenIdentifier, +) -> Result, dip721::NftError> { + SellContract::owner_of(token_identifier) +} + +#[query] +#[candid_method(query)] +pub fn owner_token_identifiers( + owner: Principal, +) -> Result, dip721::NftError> { + SellContract::owner_token_identifiers(owner) +} + +#[query] +#[candid_method(query)] +pub fn owner_token_metadata( + owner: Principal, +) -> Result, dip721::NftError> { + SellContract::owner_token_metadata(owner) +} + +#[query] +#[candid_method(query)] +pub fn operator_of( + token_identifier: dip721::TokenIdentifier, +) -> Result, dip721::NftError> { + SellContract::operator_of(token_identifier) +} + +#[query] +#[candid_method(query)] +pub fn operator_token_identifiers( + operator: Principal, +) -> Result, dip721::NftError> { + SellContract::operator_token_identifiers(operator) +} + +#[query] +#[candid_method(query)] +pub fn operator_token_metadata( + operator: Principal, +) -> Result, dip721::NftError> { + SellContract::operator_token_metadata(operator) +} + +#[query] +#[candid_method(query)] +pub fn supported_interfaces() -> Vec { + SellContract::supported_interfaces() +} + +#[query] +#[candid_method(query)] +pub fn total_supply() -> Nat { + SellContract::total_supply() +} + +#[update] +#[candid_method(update)] +pub fn approve( + spender: Principal, + token_identifier: dip721::TokenIdentifier, +) -> Result { + SellContract::approve(spender, token_identifier) +} + +#[update] +#[candid_method(update)] +pub fn set_approval_for_all( + operator: Principal, + approved: bool, +) -> Result { + SellContract::set_approval_for_all(operator, approved) +} + +#[update] +#[candid_method(update)] +pub fn is_approved_for_all( + owner: Principal, + operator: Principal, +) -> Result { + SellContract::is_approved_for_all(owner, operator) +} + +#[update] +#[candid_method(update)] +pub async fn transfer( + to: Principal, + token_identifier: dip721::TokenIdentifier, +) -> Result { + SellContract::transfer(to, token_identifier).await +} + +#[update] +#[candid_method(update)] +pub async fn transfer_from( + from: Principal, + to: Principal, + token_identifier: dip721::TokenIdentifier, +) -> Result { + SellContract::transfer_from(from, to, token_identifier).await +} + +#[update] +#[candid_method(update)] +pub fn mint( + to: Principal, + token_identifier: dip721::TokenIdentifier, + properties: Vec<(String, dip721::GenericValue)>, +) -> Result { + SellContract::mint(to, token_identifier, properties) +} + +#[update] +#[candid_method(update)] +pub fn burn( + token_identifier: dip721::TokenIdentifier, +) -> Result { + SellContract::burn(token_identifier) +} + +#[query] +#[candid_method(query)] +pub fn transaction(tx_id: Nat) -> Result { + SellContract::transaction(tx_id) +} + +#[query] +#[candid_method(query)] +pub fn total_transactions() -> Nat { + SellContract::total_transactions() +} + #[allow(dead_code)] fn main() { // The line below generates did types and service definition from the