Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ build:
mkdir -p build
cdt-cpp -abigen -contract=atomicassets -I./include src/atomicassets.cpp -o build/atomicassets.wasm
$(MAKE) build-test-consumer
$(MAKE) build-evil-renter

# Test-only fixture: a minimal EXTERNAL contract that reads atomicassets tables
# through include/atomicassets-interface.hpp. Catches header regressions the
Expand All @@ -12,6 +13,14 @@ build-test-consumer:
mkdir -p build
cdt-cpp -abigen -contract=ifaceconsumr -I./include tests/fixtures/interface-consumer/interface-consumer.cpp -o build/interface-consumer.wasm

# Test-only adversary: a renter/collection contract that throws on the
# atomicassets::logreclaim notification. Proves the permissionless reclaim can no
# longer be vetoed by a hostile notification handler (the asset-trap fix).
# Consumed by tests/asset-actions/renting-invariants.test.js; NOT a release artifact.
build-evil-renter:
mkdir -p build
cdt-cpp -abigen -contract=evilrenter -I./include tests/fixtures/evil-renter/evil-renter.cpp -o build/evil-renter.wasm

# Release-only ABI normalization. CDT 4.1 changed two -abigen spellings
# (pair fields first/second; vector<uint8_t> as `bytes`) that break existing
# integrations. The VeRT test suite is written against the raw CDT 4.1 abi, so we
Expand All @@ -33,6 +42,6 @@ export-memory:
wat2wasm -o build/atomicassets.wasm atomicassets.wat
rm atomicassets.wat

.PHONY: build build-test-consumer patch-abi release export-memory clean
.PHONY: build build-test-consumer build-evil-renter patch-abi release export-memory clean
clean:
-rm -rf build
24 changes: 15 additions & 9 deletions include/atomicassets-interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,17 +148,23 @@ namespace atomicassets {
typedef multi_index <name("assets"), assets_s> assets_t;


struct holders_s {
struct leases_s {
uint64_t asset_id;
name holder;
name owner;
name title_owner;
name renter;
name collection_name;
uint32_t rental_start;
uint32_t rental_end;
uint64_t rental_id;

uint64_t primary_key() const { return asset_id; };
uint64_t by_holder() const { return holder.value; };
uint64_t primary_key() const { return asset_id; };
uint64_t by_title_owner() const { return title_owner.value; };
uint64_t by_rental_end() const { return (uint64_t) rental_end; };
};
typedef multi_index <name("holders"), holders_s,
indexed_by<name("holder"), const_mem_fun <holders_s, uint64_t, &holders_s::by_holder>>>
holders_t;
typedef multi_index <name("leases"), leases_s,
indexed_by<name("titleowner"), const_mem_fun <leases_s, uint64_t, &leases_s::by_title_owner>>,
indexed_by<name("rentalend"), const_mem_fun <leases_s, uint64_t, &leases_s::by_rental_end>>>
leases_t;


struct offers_s {
Expand Down Expand Up @@ -228,6 +234,6 @@ namespace atomicassets {
template_mutables_t get_template_mutables(name collection_name) {return template_mutables_t(ATOMICASSETS_ACCOUNT, collection_name.value);}

assets_t get_assets(name owner) {return assets_t(ATOMICASSETS_ACCOUNT, owner.value);}
holders_t get_holders() {return holders_t(ATOMICASSETS_ACCOUNT, ATOMICASSETS_ACCOUNT.value);}
leases_t get_leases() {return leases_t(ATOMICASSETS_ACCOUNT, ATOMICASSETS_ACCOUNT.value);}

};
111 changes: 90 additions & 21 deletions include/atomicassets.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ using namespace atomicdata;
static constexpr double MAX_MARKET_FEE = 0.15;
static constexpr uint32_t AUTHOR_SWAP_TIME_DELTA = 60 * 60 * 24 * 7; // 1 week, valid for 1 week

// Protocol ceiling on a lease (and its total extended window, from the fixed rental_start), so a
// compromised or buggy rental_market can't mint a near-permanent lock. AtomicMarket caps to the same.
static constexpr uint32_t MAX_LEASE_SECONDS = 60 * 60 * 24 * 28; // 28 days

static constexpr char COLLECTION_NOT_FOUND[] = "No collection with this name exists";

CONTRACT atomicassets : public contract {
Expand All @@ -34,14 +38,32 @@ CONTRACT atomicassets : public contract {
string memo
);

ACTION move(
name owner,
name from,
name to,
vector <uint64_t> asset_ids,
ACTION setrentmkt(
name rental_market
);

ACTION setleasecap(
uint32_t max_lease_seconds
);

ACTION leasestart(
name title_owner,
name renter,
uint64_t asset_id,
uint32_t rental_end,
uint64_t rental_id,
string memo
);

ACTION leaseextend(
uint64_t asset_id,
uint32_t rental_end
);

ACTION reclaim(
uint64_t asset_id
);

ACTION createcol(
name author,
name collection_name,
Expand Down Expand Up @@ -260,13 +282,22 @@ CONTRACT atomicassets : public contract {
string memo
);

ACTION logmove(
ACTION loglock(
name collection_name,
name owner,
name from,
name to,
vector <uint64_t> asset_ids,
string memo
uint64_t asset_id,
name title_owner,
name renter,
uint32_t rental_start,
uint32_t rental_end,
uint64_t rental_id
);

ACTION logreclaim(
name collection_name,
uint64_t asset_id,
name title_owner,
name renter,
uint64_t rental_id
);

ACTION lognewoffer(
Expand Down Expand Up @@ -438,17 +469,27 @@ CONTRACT atomicassets : public contract {
typedef multi_index <name("assets"), assets_s> assets_t;


TABLE holders_s {
// Non-custodial rental "title" / lock record. A row exists for an asset_id iff
// it is actively leased: the renter is the real AtomicAssets owner, the asset
// is LOCKED (no transfer/burn/offer-out/sale), and title_owner holds the
// reclaim right until rental_end.
TABLE leases_s {
uint64_t asset_id;
name holder;
name owner;
name title_owner; // lister; reclaim returns the asset here
name renter; // current AA owner during the lease
name collection_name;
uint32_t rental_start; // sec_since_epoch the lease was first opened (fixed across extensions)
Comment on lines +476 to +481

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed the PR description: the leases row is {asset_id, title_owner, renter, rental_start, rental_end} with no market field. The single rental market lives in the rentalcfg singleton (set via setrentmkt), so it isn't stored per-lease. Code was correct; the description was stale.

Comment on lines 477 to +481
uint32_t rental_end; // sec_since_epoch the lease expires
uint64_t rental_id; // opaque market-side rental id, echoed in loglock/logreclaim

uint64_t primary_key() const { return asset_id; };
uint64_t by_holder() const { return holder.value; };
uint64_t primary_key() const { return asset_id; };
uint64_t by_title_owner() const { return title_owner.value; };
uint64_t by_rental_end() const { return (uint64_t) rental_end; };
};
typedef multi_index <name("holders"), holders_s,
indexed_by<name("holder"), const_mem_fun <holders_s, uint64_t, &holders_s::by_holder>>>
holders_t;
typedef multi_index <name("leases"), leases_s,
indexed_by<name("titleowner"), const_mem_fun <leases_s, uint64_t, &leases_s::by_title_owner>>,
indexed_by<name("rentalend"), const_mem_fun <leases_s, uint64_t, &leases_s::by_rental_end>>>
leases_t;


TABLE offers_s {
Expand Down Expand Up @@ -491,6 +532,19 @@ CONTRACT atomicassets : public contract {
typedef singleton <name("config"), config_s> config_t;


// The single account authorized to open/manage leases (leasestart/leaseextend), in its own
// singleton so it needs no config migration. Leasing is opt-in: name("") (the default, and an
// absent row) means disabled, so a fresh deploy is off until setrentmkt("atomicmarket"); set it
// back to name("") to kill-switch all leasing. Not hardcoded - the market account differs per chain.
TABLE rentalcfg_s {
name rental_market = name("");
// Governance-settable lease-duration cap, bounded above by the compile-time
// MAX_LEASE_SECONDS protocol ceiling (see setleasecap).
uint32_t max_lease_seconds = MAX_LEASE_SECONDS;
};
Comment on lines +535 to +544
typedef singleton <name("rentalcfg"), rentalcfg_s> rentalcfg_t;


TABLE tokenconfigs_s {
name standard = name("atomicassets");
std::string version = string("2.0.0");
Expand All @@ -509,6 +563,7 @@ CONTRACT atomicassets : public contract {
offers_t get_offers() {return offers_t(get_self(), get_self().value);}
balances_t get_balances() {return balances_t(get_self(), get_self().value);}
config_t get_config() {return config_t(get_self(), get_self().value);}
rentalcfg_t get_rentalcfg() {return rentalcfg_t(get_self(), get_self().value);}
tokenconfigs_t get_tokenconfigs() {return tokenconfigs_t(get_self(), get_self().value);}

schemas_t get_schemas(name collection_name) {return schemas_t(get_self(), collection_name.value);}
Expand All @@ -518,7 +573,7 @@ CONTRACT atomicassets : public contract {
template_mutables_t get_template_mutables(name collection_name) {return template_mutables_t(get_self(), collection_name.value);}

assets_t get_assets(name owner) {return assets_t(get_self(), owner.value);}
holders_t get_holders() {return holders_t(get_self(), get_self().value);}
leases_t get_leases() {return leases_t(get_self(), get_self().value);}

/*
**************************
Expand All @@ -542,7 +597,8 @@ CONTRACT atomicassets : public contract {
name to,
vector <uint64_t> asset_ids,
string memo,
name scope_payer
name scope_payer,
bool enforce_lock = true
);

void internal_decrease_balance(
Expand All @@ -560,6 +616,19 @@ CONTRACT atomicassets : public contract {
name & collection_name
);

// Reverts if the asset has a live lease/title record (i.e. is rental-locked).
void check_not_leased(uint64_t asset_id);

// Requires the authorization of the configured rental market (the single
// account allowed to open/manage leases) and returns it.
name check_rental_market();

// Emits the loglock action (shared by leasestart and leaseextend).
void send_loglock(name collection_name, uint64_t asset_id, name title_owner, name renter,
uint32_t rental_start, uint32_t rental_end, uint64_t rental_id);

uint32_t get_lease_cap();

void notify_collection_accounts(
name collection_name
);
Expand Down
Loading
Loading