Skip to content

Commit e9371ad

Browse files
authored
NFT Mint (#108)
1 parent f1f4ddd commit e9371ad

File tree

17 files changed

+450
-295
lines changed

17 files changed

+450
-295
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ members = [
1414
"testing/fil_token_integration/actors/basic_token_actor",
1515
"testing/fil_token_integration/actors/basic_receiving_actor",
1616
"testing/fil_token_integration/actors/basic_nft_actor",
17-
"testing/fil_token_integration/actors/basic_transfer_actor"
17+
"testing/fil_token_integration/actors/basic_transfer_actor",
1818
]

frc46_token/src/token/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub enum TokenError {
1818
InvalidNegative { name: &'static str, amount: TokenAmount },
1919
#[error("amount {amount:?} for {name:?} must be a multiple of {granularity:?}")]
2020
InvalidGranularity { name: &'static str, amount: TokenAmount, granularity: u64 },
21-
#[error("error calling receiver hook: {0}")]
21+
#[error("error calling other actor: {0}")]
2222
Messaging(#[from] MessagingError),
2323
#[error("receiver hook aborted when {operator:?} sent {amount:?} to {to:?} from {from:?} with exit code {exit_code:?}")]
2424
ReceiverHook {

frc46_token/src/token/mod.rs

Lines changed: 35 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,12 @@ use std::ops::Neg;
22

33
use cid::Cid;
44
pub use error::TokenError;
5-
use fvm_actor_utils::messaging::{
6-
Messaging, MessagingError, Result as MessagingResult, RECEIVER_HOOK_METHOD_NUM,
7-
};
5+
use fvm_actor_utils::messaging::{Messaging, MessagingError, RECEIVER_HOOK_METHOD_NUM};
86
use fvm_ipld_blockstore::Blockstore;
97
use fvm_ipld_encoding::RawBytes;
108
use fvm_shared::address::Address;
119
use fvm_shared::econ::TokenAmount;
1210
use fvm_shared::error::ExitCode;
13-
use fvm_shared::ActorID;
1411
use num_traits::Zero;
1512

1613
use self::state::{StateError as TokenStateError, TokenState};
@@ -156,9 +153,9 @@ where
156153
) -> Result<ReceiverHook<MintReturn>> {
157154
let amount = validate_amount_with_granularity(amount, "mint", self.granularity)?;
158155
// init the operator account so that its actor ID can be referenced in the receiver hook
159-
let operator_id = self.resolve_or_init(operator)?;
156+
let operator_id = self.msg.resolve_or_init(operator)?;
160157
// init the owner account as allowance and balance checks are not performed for minting
161-
let owner_id = self.resolve_or_init(initial_owner)?;
158+
let owner_id = self.msg.resolve_or_init(initial_owner)?;
162159

163160
// Increase the balance of the actor and increase total supply
164161
let result = self.transaction(|state, bs| {
@@ -194,7 +191,7 @@ where
194191
pub fn balance_of(&self, owner: &Address) -> Result<TokenAmount> {
195192
// Don't instantiate an account if unable to resolve to an ID address, as non-initialized
196193
// addresses have an implicit zero balance
197-
match self.get_id(owner) {
194+
match self.msg.resolve_id(owner) {
198195
Ok(owner) => Ok(self.state.get_balance(&self.bs, owner)?),
199196
Err(MessagingError::AddressNotResolved(_)) => {
200197
// uninitialized address has implicit zero balance
@@ -211,7 +208,7 @@ where
211208
pub fn allowance(&self, owner: &Address, operator: &Address) -> Result<TokenAmount> {
212209
// Don't instantiate an account if unable to resolve owner-ID, as non-initialized addresses
213210
// give implicit zero allowances to all addresses
214-
let owner = match self.get_id(owner) {
211+
let owner = match self.msg.resolve_id(owner) {
215212
Ok(owner) => owner,
216213
Err(MessagingError::AddressNotResolved(_)) => {
217214
return Ok(TokenAmount::zero());
@@ -221,7 +218,7 @@ where
221218

222219
// Don't instantiate an account if unable to resolve operator-ID, as non-initialized
223220
// addresses have an implicit zero allowance
224-
let operator = match self.get_id(operator) {
221+
let operator = match self.msg.resolve_id(operator) {
225222
Ok(operator) => operator,
226223
Err(MessagingError::AddressNotResolved(_)) => {
227224
return Ok(TokenAmount::zero());
@@ -249,8 +246,8 @@ where
249246
let delta = validate_allowance(delta, "increase allowance delta")?;
250247

251248
// Attempt to instantiate the accounts if they don't exist
252-
let owner = self.resolve_or_init(owner)?;
253-
let operator = self.resolve_or_init(operator)?;
249+
let owner = self.msg.resolve_or_init(owner)?;
250+
let operator = self.msg.resolve_or_init(operator)?;
254251
let new_amount = self.state.change_allowance_by(&self.bs, owner, operator, delta)?;
255252

256253
Ok(new_amount)
@@ -273,8 +270,8 @@ where
273270
let delta = validate_allowance(delta, "decrease allowance delta")?;
274271

275272
// Attempt to instantiate the accounts if they don't exist
276-
let owner = self.resolve_or_init(owner)?;
277-
let operator = self.resolve_or_init(operator)?;
273+
let owner = self.msg.resolve_or_init(owner)?;
274+
let operator = self.msg.resolve_or_init(operator)?;
278275
let new_allowance =
279276
self.state.change_allowance_by(&self.bs, owner, operator, &delta.neg())?;
280277

@@ -283,15 +280,15 @@ where
283280

284281
/// Sets the allowance between owner and operator to zero, returning the old allowance
285282
pub fn revoke_allowance(&mut self, owner: &Address, operator: &Address) -> Result<TokenAmount> {
286-
let owner = match self.get_id(owner) {
283+
let owner = match self.msg.resolve_id(owner) {
287284
Ok(owner) => owner,
288285
Err(MessagingError::AddressNotResolved(_)) => {
289286
// uninitialized address has implicit zero allowance already
290287
return Ok(TokenAmount::zero());
291288
}
292289
Err(e) => return Err(e.into()),
293290
};
294-
let operator = match self.get_id(operator) {
291+
let operator = match self.msg.resolve_id(operator) {
295292
Ok(operator) => operator,
296293
Err(MessagingError::AddressNotResolved(_)) => {
297294
// uninitialized address has implicit zero allowance already
@@ -318,8 +315,8 @@ where
318315
}
319316

320317
// Attempt to instantiate the accounts if they don't exist
321-
let owner = self.resolve_or_init(owner)?;
322-
let operator = self.resolve_or_init(operator)?;
318+
let owner = self.msg.resolve_or_init(owner)?;
319+
let operator = self.msg.resolve_or_init(operator)?;
323320

324321
// if both accounts resolved, explicitly set allowance
325322
Ok(self.state.set_allowance(&self.bs, owner, operator, amount)?)
@@ -338,7 +335,7 @@ where
338335
pub fn burn(&mut self, owner: &Address, amount: &TokenAmount) -> Result<BurnReturn> {
339336
let amount = validate_amount_with_granularity(amount, "burn", self.granularity)?;
340337

341-
let owner = self.resolve_or_init(owner)?;
338+
let owner = self.msg.resolve_or_init(owner)?;
342339
self.transaction(|state, bs| {
343340
// attempt to burn the requested amount
344341
let new_amount = state.change_balance_by(&bs, owner, &amount.clone().neg())?;
@@ -369,12 +366,12 @@ where
369366
amount: &TokenAmount,
370367
) -> Result<BurnFromReturn> {
371368
let amount = validate_amount_with_granularity(amount, "burn", self.granularity)?;
372-
if self.same_address(operator, owner) {
369+
if self.msg.same_address(operator, owner) {
373370
return Err(TokenError::InvalidOperator(*operator));
374371
}
375372

376373
// operator must exist to have a non-zero allowance
377-
let operator = match self.get_id(operator) {
374+
let operator = match self.msg.resolve_id(operator) {
378375
Ok(operator) => operator,
379376
Err(MessagingError::AddressNotResolved(addr)) => {
380377
// if not resolved, implicit zero allowance is not permitted to burn, so return an
@@ -391,7 +388,7 @@ where
391388
};
392389

393390
// owner must exist to have set a non-zero allowance
394-
let owner = match self.get_id(owner) {
391+
let owner = match self.msg.resolve_id(owner) {
395392
Ok(owner) => owner,
396393
Err(MessagingError::AddressNotResolved(addr)) => {
397394
return Err(TokenStateError::InsufficientAllowance {
@@ -441,8 +438,8 @@ where
441438
let amount = validate_amount_with_granularity(amount, "transfer", self.granularity)?;
442439

443440
// owner-initiated transfer
444-
let from = self.resolve_or_init(from)?;
445-
let to_id = self.resolve_or_init(to)?;
441+
let from = self.msg.resolve_or_init(from)?;
442+
let to_id = self.msg.resolve_or_init(to)?;
446443
// skip allowance check for self-managed transfers
447444
let res = self.transaction(|state, bs| {
448445
// don't change balance if to == from, but must check that the transfer doesn't exceed balance
@@ -507,12 +504,12 @@ where
507504
token_data: RawBytes,
508505
) -> Result<ReceiverHook<TransferFromReturn>> {
509506
let amount = validate_amount_with_granularity(amount, "transfer", self.granularity)?;
510-
if self.same_address(operator, from) {
507+
if self.msg.same_address(operator, from) {
511508
return Err(TokenError::InvalidOperator(*operator));
512509
}
513510

514511
// operator-initiated transfer must have a resolvable operator
515-
let operator_id = match self.get_id(operator) {
512+
let operator_id = match self.msg.resolve_id(operator) {
516513
// if operator resolved, we can continue with other checks
517514
Ok(id) => id,
518515
// if we cannot resolve the operator, they are forbidden to transfer
@@ -528,7 +525,7 @@ where
528525
};
529526

530527
// the owner must exist to have specified a non-zero allowance
531-
let from = match self.get_id(from) {
528+
let from = match self.msg.resolve_id(from) {
532529
Ok(id) => id,
533530
Err(MessagingError::AddressNotResolved(from)) => {
534531
return Err(TokenError::TokenState(TokenStateError::InsufficientAllowance {
@@ -542,7 +539,7 @@ where
542539
};
543540

544541
// attempt to initialize the receiving account if not present
545-
let to_id = self.resolve_or_init(to)?;
542+
let to_id = self.msg.resolve_or_init(to)?;
546543

547544
// update token state
548545
let ret = self.transaction(|state, bs| {
@@ -595,7 +592,7 @@ where
595592
/// hook on recipient accounts. Returns the old balance.
596593
pub fn set_balance(&mut self, owner: &Address, amount: &TokenAmount) -> Result<TokenAmount> {
597594
let amount = validate_amount_with_granularity(amount, "set_balance", self.granularity)?;
598-
let owner = self.resolve_or_init(owner)?;
595+
let owner = self.msg.resolve_or_init(owner)?;
599596
let old_balance =
600597
self.transaction(|state, bs| Ok(state.set_balance(bs, owner, amount)?))?;
601598
Ok(old_balance)
@@ -607,49 +604,6 @@ where
607604
BS: Blockstore,
608605
MSG: Messaging,
609606
{
610-
/// Resolves an address to an ID address, sending a message to initialise an account there if
611-
/// it doesn't exist
612-
///
613-
/// If the account cannot be created, this function returns MessagingError::AddressNotInitialized
614-
fn resolve_or_init(&self, address: &Address) -> MessagingResult<ActorID> {
615-
let id = match self.msg.resolve_id(address) {
616-
Ok(addr) => addr,
617-
Err(MessagingError::AddressNotResolved(_e)) => self.msg.initialize_account(address)?,
618-
Err(e) => return Err(e),
619-
};
620-
Ok(id)
621-
}
622-
623-
/// Attempts to resolve an address to an ActorID, returning MessagingError::AddressNotResolved
624-
/// if it wasn't found
625-
fn get_id(&self, address: &Address) -> MessagingResult<ActorID> {
626-
self.msg.resolve_id(address)
627-
}
628-
629-
/// Attempts to compare two addresses, seeing if they would resolve to the same Actor without
630-
/// actually initiating accounts for them
631-
///
632-
/// If a and b are of the same type, simply do an equality check. Otherwise, attempt to resolve
633-
/// to an ActorID and compare
634-
fn same_address(&self, address_a: &Address, address_b: &Address) -> bool {
635-
let protocol_a = address_a.protocol();
636-
let protocol_b = address_b.protocol();
637-
if protocol_a == protocol_b {
638-
address_a == address_b
639-
} else {
640-
// attempt to resolve both to ActorID
641-
let id_a = match self.get_id(address_a) {
642-
Ok(id) => id,
643-
Err(_) => return false,
644-
};
645-
let id_b = match self.get_id(address_b) {
646-
Ok(id) => id,
647-
Err(_) => return false,
648-
};
649-
id_a == id_b
650-
}
651-
}
652-
653607
/// Calls the receiver hook, returning the result
654608
pub fn call_receiver_hook(
655609
&mut self,
@@ -1078,7 +1032,7 @@ mod test {
10781032
FRC46TokenReceived {
10791033
operator: TOKEN_ACTOR.id().unwrap(),
10801034
from: TOKEN_ACTOR.id().unwrap(),
1081-
to: token.get_id(&secp_address).unwrap(),
1035+
to: token.msg.resolve_id(&secp_address).unwrap(),
10821036
amount: TokenAmount::from_atto(1_000_000),
10831037
operator_data: Default::default(),
10841038
token_data: Default::default(),
@@ -1113,7 +1067,7 @@ mod test {
11131067
FRC46TokenReceived {
11141068
operator: TOKEN_ACTOR.id().unwrap(),
11151069
from: TOKEN_ACTOR.id().unwrap(),
1116-
to: token.get_id(&bls_address).unwrap(),
1070+
to: token.msg.resolve_id(&bls_address).unwrap(),
11171071
amount: TokenAmount::from_atto(1_000_000),
11181072
operator_data: Default::default(),
11191073
token_data: Default::default(),
@@ -1506,7 +1460,7 @@ mod test {
15061460
FRC46TokenReceived {
15071461
operator: ALICE.id().unwrap(),
15081462
from: ALICE.id().unwrap(),
1509-
to: token.get_id(secp_address).unwrap(),
1463+
to: token.msg.resolve_id(secp_address).unwrap(),
15101464
amount: TokenAmount::from_atto(10),
15111465
operator_data: Default::default(),
15121466
token_data: Default::default(),
@@ -1557,7 +1511,7 @@ mod test {
15571511
assert_eq!(token.total_supply(), TokenAmount::zero());
15581512

15591513
// secp_address was initialized
1560-
assert!(token.get_id(secp_address).is_ok());
1514+
assert!(token.msg.resolve_id(secp_address).is_ok());
15611515

15621516
let actor_address = &actor_address();
15631517
// transfers from actors fail with uninitializable
@@ -1584,7 +1538,7 @@ mod test {
15841538
assert_eq!(token.total_supply(), TokenAmount::zero());
15851539

15861540
// actor address was not initialized
1587-
assert!(token.get_id(actor_address).is_err());
1541+
assert!(token.msg.resolve_id(actor_address).is_err());
15881542
token.check_invariants().unwrap();
15891543
}
15901544

@@ -2527,13 +2481,13 @@ mod test {
25272481
assert_eq!(allowance, TokenAmount::zero());
25282482

25292483
// accounts were not initialized
2530-
let err = token.get_id(bls).unwrap_err();
2484+
let err = token.msg.resolve_id(bls).unwrap_err();
25312485
if let MessagingError::AddressNotResolved(e) = err {
25322486
assert_eq!(e, *bls);
25332487
} else {
25342488
panic!("expected AddressNotResolved error");
25352489
}
2536-
let err = token.get_id(secp).unwrap_err();
2490+
let err = token.msg.resolve_id(secp).unwrap_err();
25372491
if let MessagingError::AddressNotResolved(e) = err {
25382492
assert_eq!(e, *secp);
25392493
} else {
@@ -2547,13 +2501,13 @@ mod test {
25472501
assert_eq!(balance, TokenAmount::zero());
25482502

25492503
// accounts were not initialized
2550-
let err = token.get_id(bls).unwrap_err();
2504+
let err = token.msg.resolve_id(bls).unwrap_err();
25512505
if let MessagingError::AddressNotResolved(e) = err {
25522506
assert_eq!(e, *bls);
25532507
} else {
25542508
panic!("expected AddressNotResolved error");
25552509
}
2556-
let err = token.get_id(secp).unwrap_err();
2510+
let err = token.msg.resolve_id(secp).unwrap_err();
25572511
if let MessagingError::AddressNotResolved(e) = err {
25582512
assert_eq!(e, *secp);
25592513
} else {
@@ -2651,7 +2605,7 @@ mod test {
26512605
}
26522606
};
26532607

2654-
if token.same_address(operator, from) {
2608+
if token.msg.same_address(operator, from) {
26552609
let res = token.transfer(
26562610
from,
26572611
operator,

frcxx_nft/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ edition = "2021"
77
[dependencies]
88
cid = { version = "0.8.3", default-features = false, features = ["serde-codec"] }
99
frc42_dispatch = { path = "../frc42_dispatch" }
10+
fvm_actor_utils = { version = "0.1.0", path = "../fvm_actor_utils" }
1011
fvm_ipld_blockstore = "0.1.1"
1112
fvm_ipld_hamt = "0.5.1"
1213
fvm_ipld_amt = { version = "0.4.2", features = ["go-interop"] }
1314
fvm_ipld_encoding = "0.2.2"
14-
fvm_sdk = { version = "1.0.0" }
15-
fvm_shared = { version = "0.8.0" }
15+
fvm_sdk = { version = "2.0.0-alpha.2" }
16+
fvm_shared = { version = "2.0.0-alpha.2" }
1617
num-traits = { version = "0.2.15" }
1718
serde = { version = "1.0.136", features = ["derive"] }
1819
serde_tuple = { version = "0.5.0" }

frcxx_nft/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# frcxx_nft
2+
3+
This acts as the reference library for FRCXX. While remaining complaint with the
4+
spec, this library is opinionated in its batching, minting and storage
5+
strategies to optimize for common usage patterns.
6+
7+
For example, write operations are generally optimised over read operations as
8+
on-chain state can be read by direct inspection (rather than via an actor call)
9+
in many cases.

0 commit comments

Comments
 (0)