From dd3eda78780255b89fbc7f5acd6b3a27089ec4b2 Mon Sep 17 00:00:00 2001 From: Nicholas Shellabarger Date: Fri, 16 Sep 2022 22:26:51 -0600 Subject: [PATCH 1/6] use stake --- interface.rsh | 218 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 133 insertions(+), 85 deletions(-) diff --git a/interface.rsh b/interface.rsh index 0744590..9b1910f 100644 --- a/interface.rsh +++ b/interface.rsh @@ -3,8 +3,7 @@ // ----------------------------------------------- // Name: KINN Active Reverse Auction (A1) -// Author: Nicholas Shellabarger -// Version: 1.2.3 - add bid fee and unlock api +// Version: 1.2.4 - use stake // Requires Reach v0.1.11-rc7 (27cb9643) or later // ----------------------------------------------- // TODO calculate price change per second with more precision @@ -13,17 +12,67 @@ import { min, max } from "@nash-protocol/starter-kit#lite-v0.1.9r1:util.rsh"; +import { rStake, rUnstake } from "@KinnFoundation/stake#stake-v0.1.11r0:interface.rsh"; + // CONSTS const SERIAL_VER = 0; // serial version of reach app reserved to release identical contracts under a separate plana id const DIST_LENGTH = 8; // number of slots to distribute proceeds after sale -const FEE_MIN_ACCEPT = 9_000; // 0.006 -const FEE_MIN_CONSTRUCT = 7_000; // 0.005 -const FEE_MIN_RELAY = 1_7000; // 0.017 +const FEE_MIN_ACCEPT = 9_000; // 0.009 +const FEE_MIN_CONSTRUCT = 7_000; // 0.007 +const FEE_MIN_RELAY = 17_000; // 0.017 const FEE_MIN_CURATOR = 10_000; // 0.1 -const FEE_MIN_BID = 1_000; // 0.001 +const FEE_MIN_BID = 10_000; // 0.001 +const FEE_MIN_ACTIVE_BID = 1; // some 1 + + +// TYPES + +const Params = Object({ + tokenAmount: UInt, // NFT token amount + startPrice: UInt, // 100 + floorPrice: UInt, // 1 + endSecs: UInt, // 1 + addrs: Array(Address, DIST_LENGTH), // [addr, addr, addr, addr, addr, addr, addr, addr, addr, addr] + distr: Array(UInt, DIST_LENGTH), // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + royaltyCap: UInt, // 10 + acceptFee: UInt, // 0.008 + constructFee: UInt, // 0.006 + relayFee: UInt, // 0.007 + curatorFee: UInt, // 0.1 + bidFee: UInt, // 0.001 + activeBidFee: UInt, // 0.001 +}); + +const State = Struct([ + ["manager", Address], + ["token", Token], + ["tokenAmount", UInt], + ["currentPrice", UInt], + ["startPrice", UInt], + ["floorPrice", UInt], + ["closed", Bool], + ["endSecs", UInt], + ["priceChangePerSec", UInt], + ["addrs", Array(Address, DIST_LENGTH)], + ["distr", Array(UInt, DIST_LENGTH)], + ["royaltyCap", UInt], + ["who", Address], + ["partTake", UInt], + ["acceptFee", UInt], + ["constructFee", UInt], + ["relayFee", UInt], + ["curatorFee", UInt], + ["curatorAddr", Address], + ["timestamp", UInt], + ["activeToken", Token], + ["activeAmount", UInt], + ["activeAddr", Address], + ["activeCtc", Contract], + ["activeBidFee", UInt], +]); // FUNCS @@ -76,28 +125,15 @@ const safePercent = (amount, percentage, percentPrecision) => // INTERACTS -const relayInteract = {}; - -const Params = Object({ - tokenAmount: UInt, // NFT token amount - startPrice: UInt, // 100 - floorPrice: UInt, // 1 - endSecs: UInt, // 1 - addrs: Array(Address, DIST_LENGTH), // [addr, addr, addr, addr, addr, addr, addr, addr, addr, addr] - distr: Array(UInt, DIST_LENGTH), // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - royaltyCap: UInt, // 10 - acceptFee: UInt, // 0.008 - constructFee: UInt, // 0.006 - relayFee: UInt, // 0.007 - curatorFee: UInt, // 0.1 - bidFee: UInt, // 0.001 -}); - const auctioneerInteract = { getParams: Fun([], Params), signal: Fun([], Null), }; +const relayInteract = {}; + +// CONTRACT + export const Event = () => []; export const Participants = () => [ @@ -105,32 +141,6 @@ export const Participants = () => [ ParticipantClass("Relay", relayInteract), ]; -const State = Struct([ - ["manager", Address], - ["token", Token], - ["tokenAmount", UInt], - ["currentPrice", UInt], - ["startPrice", UInt], - ["floorPrice", UInt], - ["closed", Bool], - ["endSecs", UInt], - ["priceChangePerSec", UInt], - ["addrs", Array(Address, DIST_LENGTH)], - ["distr", Array(UInt, DIST_LENGTH)], - ["royaltyCap", UInt], - ["who", Address], - ["partTake", UInt], - ["acceptFee", UInt], - ["constructFee", UInt], - ["relayFee", UInt], - ["curatorFee", UInt], - ["curatorAddr", Address], - ["timestamp", UInt], - ["activeToken", Token], - ["activeAmount", UInt], - ["activeAddr", Address], -]); - export const Views = () => [ View({ state: State, @@ -142,8 +152,8 @@ export const Api = () => [ touch: Fun([], Null), acceptOffer: Fun([Address], Null), cancel: Fun([], Null), - bid: Fun([UInt], Null), - unlock: Fun([], Null), + bid: Fun([Contract], Null), + bidCancel: Fun([], Null), }), ]; @@ -170,6 +180,7 @@ export const App = (map) => { relayFee, curatorFee, bidFee, + activeBidFee, } = declassify(interact.getParams()); }); @@ -186,7 +197,8 @@ export const App = (map) => { constructFee, relayFee, curatorFee, - bidFee + bidFee, + activeBidFee ) .check(() => { check(tokenAmount > 0, "tokenAmount must be greater than 0"); @@ -224,10 +236,15 @@ export const App = (map) => { bidFee >= FEE_MIN_BID, "bidFee must be greater than or equal to minimum bid fee" ); + check( + activeBidFee >= FEE_MIN_ACTIVE_BID, + "activeBidFee must be greater than or equal to minimum bid fee" + ); }) .pay([ amt + (constructFee + acceptFee + relayFee + curatorFee) + SERIAL_VER, [tokenAmount, token], + [1_000_000, activeToken], ]) .timeout(relativeTime(ttl), () => { // Step @@ -235,7 +252,7 @@ export const App = (map) => { commit(); exit(); }); - transfer(amt + constructFee + SERIAL_VER).to(addr); + transfer([amt + constructFee + SERIAL_VER, [1_000_000, activeToken]]).to(addr); Auctioneer.interact.signal(); @@ -273,10 +290,10 @@ export const App = (map) => { activeToken, activeAmount: 0, activeAddr: Auctioneer, + activeCtc: getContract(), + activeBidFee, }; - v.state.set(State.fromObject(initialState)); - // Step const [state] = parallelReduce([initialState]) .define(() => { @@ -292,14 +309,7 @@ export const App = (map) => { "token balance accurate after closed" ) // ACTIVE TOKEN BALANCE - .invariant( - implies(!state.closed, balance(activeToken) == state.activeAmount), - "active token balance accurate before closed" - ) - .invariant( - implies(state.closed, balance(activeToken) == 0), - "active token balance accurate before closed" - ) + .invariant(balance(activeToken) == 0, "active token balance accurate") // BALANCE .invariant( implies(!state.closed, balance() == acceptFee + relayFee + curatorFee), @@ -309,8 +319,12 @@ export const App = (map) => { .while(!state.closed) .paySpec([activeToken]) // api: updates current price + // allows anybody to update price .api_(a.touch, () => { - check(state.currentPrice >= floorPrice); + check( + state.currentPrice >= floorPrice, + "currentPrice must be greater than or equal to floorPrice" + ); return [ (k) => { k(null); @@ -329,6 +343,12 @@ export const App = (map) => { ]; }) // api: accepts offer + // allows anybody but curator to accept offer + // transfers 1% to addr + // calculates proceeding take + // transfers reamining to seller + // transfers accept fee, diff, and token amount to buy + // transfers currator fee to curator .api_(a.acceptOffer, (cAddr) => { check(cAddr != this, "cannot accept offer as curator"); return [ @@ -348,10 +368,13 @@ export const App = (map) => { const partTake = remaining / royaltyCap; const proceedTake = partTake * distrTake; const sellerTake = remaining - proceedTake; - transfer([cent, [state.activeAmount, activeToken]]).to(addr); + transfer(cent).to(addr); transfer(sellerTake).to(Auctioneer); transfer([acceptFee + diff, [tokenAmount, token]]).to(this); transfer(curatorFee).to(cAddr); + if (getContract() != state.activeCtc) { + rUnstake(state.activeCtc); + } return [ { ...state, @@ -365,52 +388,77 @@ export const App = (map) => { }, ]; }) - // api: bid - .api_(a.bid, (msg) => { - check(msg > state.activeAmount, "bid must be greater than active amount"); + // api: cancel + // allows auctioneer to cancel auction + // transfers accept and curator fee and token(s) back to auctionee + // unstakes active token if any + .api_(a.cancel, () => { + check(this == Auctioneer, "only auctioneer can cancel"); return [ - [bidFee, [msg, activeToken]], (k) => { k(null); - transfer(bidFee).to(addr); - transfer(state.activeAmount, activeToken).to(state.activeAddr); + transfer([acceptFee + curatorFee, [tokenAmount, token]]).to(this); + if (getContract() != state.activeCtc) { + rUnstake(state.activeCtc); + } return [ { ...state, - activeAmount: msg, - activeAddr: this, + closed: true, + activeAmount: 0, + activeAddr: Auctioneer, + activeCtc: getContract(), }, ]; }, ]; }) - // api: claim - .api_(a.unlock, () => { - check(this == addr, "only master can unlock"); + // api: bid + // allows anybody to supersede the current bid + // transfer bid fee in network token and non-network token (active token) to addr + // unlock active token if any + .api_(a.bid, (ctc) => { return [ + [bidFee, [activeBidFee, activeToken]], (k) => { k(null); + transfer([bidFee, [activeBidFee, activeToken]]).to(addr); + const { manager: r1Manager, tokenAmount: r1TokenAmount } = rStake( + ctc, + activeToken, + state.activeAmount + ); + if (getContract() != state.activeCtc) { + rUnstake(state.activeCtc); + } return [ { ...state, - activeAddr: this + activeAmount: r1TokenAmount, + activeAddr: r1Manager, + activeCtc: ctc, }, ]; - } + }, ]; }) - // api: cancels auction - .api_(a.cancel, () => { - check(this == Auctioneer, "only auctioneer can cancel"); + // api: bid cancel + // allows the bidder to cancel their bid + // unstakes active token if any + .api_(a.bidCancel, () => { + check(this == state.activeAddr, "only active bidder can cancel bid"); return [ (k) => { k(null); - transfer([acceptFee + curatorFee, [tokenAmount, token]]).to(this); - transfer(state.activeAmount, activeToken).to(state.activeAddr); + if (getContract() != state.activeCtc) { + rUnstake(state.activeCtc); + } return [ { ...state, - closed: true, + activeAmount: 0, + activeAddr: Auctioneer, + activeCtc: getContract(), }, ]; }, @@ -419,8 +467,8 @@ export const App = (map) => { .timeout(false); commit(); - Relay.publish(); // Step + Relay.publish(); ((recvAmount, pDistr) => { transfer(pDistr[0]).to(state.activeAddr); // reserved transfer(pDistr[1]).to(addrs[1]); From 7ae578cfc8823b7acd23ab54ea48fd2c082b6016 Mon Sep 17 00:00:00 2001 From: Nicholas Shellabarger Date: Mon, 19 Sep 2022 15:18:53 -0600 Subject: [PATCH 2/6] use data for contract --- interface.rsh | 72 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 28 deletions(-) diff --git a/interface.rsh b/interface.rsh index 9b1910f..1cf49f0 100644 --- a/interface.rsh +++ b/interface.rsh @@ -3,7 +3,7 @@ // ----------------------------------------------- // Name: KINN Active Reverse Auction (A1) -// Version: 1.2.4 - use stake +// Version: 1.2.5 - use data for contract // Requires Reach v0.1.11-rc7 (27cb9643) or later // ----------------------------------------------- // TODO calculate price change per second with more precision @@ -24,12 +24,14 @@ const FEE_MIN_ACCEPT = 9_000; // 0.009 const FEE_MIN_CONSTRUCT = 7_000; // 0.007 const FEE_MIN_RELAY = 17_000; // 0.017 const FEE_MIN_CURATOR = 10_000; // 0.1 -const FEE_MIN_BID = 10_000; // 0.001 const FEE_MIN_ACTIVE_BID = 1; // some 1 +const FEE_MIN_ACTIVE_ACTIVATION = 1; // some 1 // TYPES +const MContract = Maybe(Contract); + const Params = Object({ tokenAmount: UInt, // NFT token amount startPrice: UInt, // 100 @@ -42,8 +44,8 @@ const Params = Object({ constructFee: UInt, // 0.006 relayFee: UInt, // 0.007 curatorFee: UInt, // 0.1 - bidFee: UInt, // 0.001 activeBidFee: UInt, // 0.001 + activeActivationFee: UInt, // 0.001 }); const State = Struct([ @@ -72,6 +74,7 @@ const State = Struct([ ["activeAddr", Address], ["activeCtc", Contract], ["activeBidFee", UInt], + ["activeActivationFee", UInt], ]); // FUNCS @@ -179,8 +182,8 @@ export const App = (map) => { constructFee, relayFee, curatorFee, - bidFee, activeBidFee, + activeActivationFee, } = declassify(interact.getParams()); }); @@ -197,8 +200,8 @@ export const App = (map) => { constructFee, relayFee, curatorFee, - bidFee, - activeBidFee + activeBidFee, + activeActivationFee ) .check(() => { check(tokenAmount > 0, "tokenAmount must be greater than 0"); @@ -232,19 +235,19 @@ export const App = (map) => { curatorFee >= FEE_MIN_CURATOR, "curatorFee must be greater than or equal to minimum curator fee" ); - check( - bidFee >= FEE_MIN_BID, - "bidFee must be greater than or equal to minimum bid fee" - ); check( activeBidFee >= FEE_MIN_ACTIVE_BID, "activeBidFee must be greater than or equal to minimum bid fee" ); + check( + activeActivationFee >= FEE_MIN_ACTIVE_ACTIVATION, + "activeActivationFee must be greater than or equal to minimum activation fee" + ); }) .pay([ amt + (constructFee + acceptFee + relayFee + curatorFee) + SERIAL_VER, [tokenAmount, token], - [1_000_000, activeToken], + [activeActivationFee, activeToken], ]) .timeout(relativeTime(ttl), () => { // Step @@ -252,7 +255,9 @@ export const App = (map) => { commit(); exit(); }); - transfer([amt + constructFee + SERIAL_VER, [1_000_000, activeToken]]).to(addr); + transfer([amt + constructFee + SERIAL_VER, [activeActivationFee, activeToken]]).to( + addr + ); Auctioneer.interact.signal(); @@ -290,12 +295,13 @@ export const App = (map) => { activeToken, activeAmount: 0, activeAddr: Auctioneer, - activeCtc: getContract(), + activeCtc: getContract(), // ref to self never used activeBidFee, + activeActivationFee, }; // Step - const [state] = parallelReduce([initialState]) + const [state, mctc] = parallelReduce([initialState, MContract.None()]) .define(() => { v.state.set(State.fromObject(state)); }) @@ -314,7 +320,7 @@ export const App = (map) => { .invariant( implies(!state.closed, balance() == acceptFee + relayFee + curatorFee), "balance accurate before close" - ) + ) // REM missing invariant balance accurate after close .while(!state.closed) .paySpec([activeToken]) @@ -338,6 +344,7 @@ export const App = (map) => { dk ), }, + mctc, ]; }, ]; @@ -372,8 +379,10 @@ export const App = (map) => { transfer(sellerTake).to(Auctioneer); transfer([acceptFee + diff, [tokenAmount, token]]).to(this); transfer(curatorFee).to(cAddr); - if (getContract() != state.activeCtc) { - rUnstake(state.activeCtc); + switch (mctc) { + case Some: + rUnstake(mctc); + case None: } return [ { @@ -384,6 +393,7 @@ export const App = (map) => { curatorAddr: cAddr, partTake, }, + mctc, ]; }, ]; @@ -398,8 +408,10 @@ export const App = (map) => { (k) => { k(null); transfer([acceptFee + curatorFee, [tokenAmount, token]]).to(this); - if (getContract() != state.activeCtc) { - rUnstake(state.activeCtc); + switch (mctc) { + case Some: + rUnstake(mctc); + case None: } return [ { @@ -407,8 +419,8 @@ export const App = (map) => { closed: true, activeAmount: 0, activeAddr: Auctioneer, - activeCtc: getContract(), }, + MContract.None(), ]; }, ]; @@ -419,17 +431,19 @@ export const App = (map) => { // unlock active token if any .api_(a.bid, (ctc) => { return [ - [bidFee, [activeBidFee, activeToken]], + [0, [activeBidFee, activeToken]], (k) => { k(null); - transfer([bidFee, [activeBidFee, activeToken]]).to(addr); + transfer([0, [activeBidFee, activeToken]]).to(addr); const { manager: r1Manager, tokenAmount: r1TokenAmount } = rStake( ctc, activeToken, state.activeAmount ); - if (getContract() != state.activeCtc) { - rUnstake(state.activeCtc); + switch (mctc) { + case Some: + rUnstake(mctc); + case None: } return [ { @@ -438,6 +452,7 @@ export const App = (map) => { activeAddr: r1Manager, activeCtc: ctc, }, + MContract.Some(ctc), ]; }, ]; @@ -450,16 +465,18 @@ export const App = (map) => { return [ (k) => { k(null); - if (getContract() != state.activeCtc) { - rUnstake(state.activeCtc); + switch (mctc) { + case Some: + rUnstake(mctc); + case None: } return [ { ...state, activeAmount: 0, activeAddr: Auctioneer, - activeCtc: getContract(), }, + MContract.None(), ]; }, ]; @@ -467,7 +484,6 @@ export const App = (map) => { .timeout(false); commit(); - // Step Relay.publish(); ((recvAmount, pDistr) => { transfer(pDistr[0]).to(state.activeAddr); // reserved From b04f856081f34b88edb24fb869940fc7eeeb3717 Mon Sep 17 00:00:00 2001 From: Nicholas Shellabarger Date: Mon, 19 Sep 2022 15:38:49 -0600 Subject: [PATCH 3/6] protect relay txn from rt --- interface.rsh | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/interface.rsh b/interface.rsh index 1cf49f0..658ef13 100644 --- a/interface.rsh +++ b/interface.rsh @@ -3,7 +3,7 @@ // ----------------------------------------------- // Name: KINN Active Reverse Auction (A1) -// Version: 1.2.5 - use data for contract +// Version: 1.2.6 - protect relay txn from rt // Requires Reach v0.1.11-rc7 (27cb9643) or later // ----------------------------------------------- // TODO calculate price change per second with more precision @@ -27,7 +27,6 @@ const FEE_MIN_CURATOR = 10_000; // 0.1 const FEE_MIN_ACTIVE_BID = 1; // some 1 const FEE_MIN_ACTIVE_ACTIVATION = 1; // some 1 - // TYPES const MContract = Maybe(Contract); @@ -255,9 +254,10 @@ export const App = (map) => { commit(); exit(); }); - transfer([amt + constructFee + SERIAL_VER, [activeActivationFee, activeToken]]).to( - addr - ); + transfer([ + amt + constructFee + SERIAL_VER, + [activeActivationFee, activeToken], + ]).to(addr); Auctioneer.interact.signal(); @@ -320,7 +320,7 @@ export const App = (map) => { .invariant( implies(!state.closed, balance() == acceptFee + relayFee + curatorFee), "balance accurate before close" - ) + ) // REM missing invariant balance accurate after close .while(!state.closed) .paySpec([activeToken]) @@ -505,6 +505,11 @@ export const App = (map) => { }); // Step Relay.publish(rAddr); + + transfer([ + [getUntrackedFunds(token), token], + [getUntrackedFunds(activeToken), activeToken], + ]).to(addr); transfer(recvAmount).to(rAddr); commit(); exit(); From 407f7e736806b9435c71907512b372e8c183dac3 Mon Sep 17 00:00:00 2001 From: Nicholas Shellabarger Date: Mon, 19 Sep 2022 16:24:26 -0600 Subject: [PATCH 4/6] protect relay txn from rt --- interface.rsh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface.rsh b/interface.rsh index 658ef13..973a168 100644 --- a/interface.rsh +++ b/interface.rsh @@ -3,7 +3,7 @@ // ----------------------------------------------- // Name: KINN Active Reverse Auction (A1) -// Version: 1.2.6 - protect relay txn from rt +// Version: 1.2.7 - protect relay txn from rt // Requires Reach v0.1.11-rc7 (27cb9643) or later // ----------------------------------------------- // TODO calculate price change per second with more precision @@ -22,7 +22,7 @@ const DIST_LENGTH = 8; // number of slots to distribute proceeds after sale const FEE_MIN_ACCEPT = 9_000; // 0.009 const FEE_MIN_CONSTRUCT = 7_000; // 0.007 -const FEE_MIN_RELAY = 17_000; // 0.017 +const FEE_MIN_RELAY = 19_000; // 0.019 const FEE_MIN_CURATOR = 10_000; // 0.1 const FEE_MIN_ACTIVE_BID = 1; // some 1 const FEE_MIN_ACTIVE_ACTIVATION = 1; // some 1 @@ -507,10 +507,10 @@ export const App = (map) => { Relay.publish(rAddr); transfer([ + recvAmount, [getUntrackedFunds(token), token], [getUntrackedFunds(activeToken), activeToken], - ]).to(addr); - transfer(recvAmount).to(rAddr); + ]).to(rAddr); commit(); exit(); })( From b418c84ce6ee7f2c994fc65961a6b5735ad0ae74 Mon Sep 17 00:00:00 2001 From: Nicholas Shellabarger Date: Wed, 21 Sep 2022 12:26:41 -0600 Subject: [PATCH 5/6] fix local key issue --- interface.rsh | 180 ++++++++++++++++++-------------------------------- 1 file changed, 64 insertions(+), 116 deletions(-) diff --git a/interface.rsh b/interface.rsh index 973a168..7689588 100644 --- a/interface.rsh +++ b/interface.rsh @@ -14,66 +14,34 @@ import { min, max } from "@nash-protocol/starter-kit#lite-v0.1.9r1:util.rsh"; import { rStake, rUnstake } from "@KinnFoundation/stake#stake-v0.1.11r0:interface.rsh"; +import { Params, State as ReverseState, MContract } from "@KinnFoundation/reverse#reverse-v0.1.11r3:interface.rsh"; + // CONSTS const SERIAL_VER = 0; // serial version of reach app reserved to release identical contracts under a separate plana id -const DIST_LENGTH = 8; // number of slots to distribute proceeds after sale +const DIST_LENGTH = 9; // number of slots to distribute proceeds after sale -const FEE_MIN_ACCEPT = 9_000; // 0.009 +const FEE_MIN_ACCEPT = 8_000; // 0.008 const FEE_MIN_CONSTRUCT = 7_000; // 0.007 -const FEE_MIN_RELAY = 19_000; // 0.019 -const FEE_MIN_CURATOR = 10_000; // 0.1 -const FEE_MIN_ACTIVE_BID = 1; // some 1 -const FEE_MIN_ACTIVE_ACTIVATION = 1; // some 1 +const FEE_MIN_RELAY = 20_000; // 0.019 + 0.001 +const FEE_MIN_ACTIVE_BID = 1; // 1au +const FEE_MIN_ACTIVE_ACTIVATION = 1; // 1au -// TYPES +const ADDR_RESERVED_ACTIVE_BIDDER = 0; +const ADDR_RESERVED_CURATOR = 1; -const MContract = Maybe(Contract); - -const Params = Object({ - tokenAmount: UInt, // NFT token amount - startPrice: UInt, // 100 - floorPrice: UInt, // 1 - endSecs: UInt, // 1 - addrs: Array(Address, DIST_LENGTH), // [addr, addr, addr, addr, addr, addr, addr, addr, addr, addr] - distr: Array(UInt, DIST_LENGTH), // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - royaltyCap: UInt, // 10 - acceptFee: UInt, // 0.008 - constructFee: UInt, // 0.006 - relayFee: UInt, // 0.007 - curatorFee: UInt, // 0.1 - activeBidFee: UInt, // 0.001 - activeActivationFee: UInt, // 0.001 -}); +// TYPES -const State = Struct([ - ["manager", Address], - ["token", Token], - ["tokenAmount", UInt], - ["currentPrice", UInt], - ["startPrice", UInt], - ["floorPrice", UInt], - ["closed", Bool], - ["endSecs", UInt], - ["priceChangePerSec", UInt], - ["addrs", Array(Address, DIST_LENGTH)], - ["distr", Array(UInt, DIST_LENGTH)], - ["royaltyCap", UInt], - ["who", Address], - ["partTake", UInt], - ["acceptFee", UInt], - ["constructFee", UInt], - ["relayFee", UInt], - ["curatorFee", UInt], - ["curatorAddr", Address], - ["timestamp", UInt], +const ActiveState = Struct([ ["activeToken", Token], ["activeAmount", UInt], - ["activeAddr", Address], ["activeCtc", Contract], - ["activeBidFee", UInt], - ["activeActivationFee", UInt], +]); + +const State = Struct([ + ...Struct.fields(ReverseState(DIST_LENGTH)), + ...Struct.fields(ActiveState) ]); // FUNCS @@ -86,7 +54,6 @@ const precision = 1000000; // 10 ^ 6 /* * calculate price based on seconds elapsed since reference secs */ - const priceFunc = (secs) => (startPrice, floorPrice, referenceConcensusSecs, dk) => max( @@ -128,7 +95,7 @@ const safePercent = (amount, percentage, percentPrecision) => // INTERACTS const auctioneerInteract = { - getParams: Fun([], Params), + getParams: Fun([], Params(DIST_LENGTH)), signal: Fun([], Null), }; @@ -177,12 +144,6 @@ export const App = (map) => { addrs, distr, royaltyCap, - acceptFee, - constructFee, - relayFee, - curatorFee, - activeBidFee, - activeActivationFee, } = declassify(interact.getParams()); }); @@ -194,13 +155,7 @@ export const App = (map) => { endSecs, addrs, distr, - royaltyCap, - acceptFee, - constructFee, - relayFee, - curatorFee, - activeBidFee, - activeActivationFee + royaltyCap ) .check(() => { check(tokenAmount > 0, "tokenAmount must be greater than 0"); @@ -218,35 +173,11 @@ export const App = (map) => { royaltyCap == (10 * floorPrice) / 1000000, "royaltyCap must be 10x of floorPrice" ); - check( - acceptFee >= FEE_MIN_ACCEPT, - "acceptFee must be greater than or equal to minimum accept fee" - ); - check( - constructFee >= FEE_MIN_CONSTRUCT, - "constructFee must be greater than or equal to minimum construct fee" - ); - check( - relayFee >= FEE_MIN_RELAY, - "relayFee must be greater than or equal to minimum relay fee" - ); - check( - curatorFee >= FEE_MIN_CURATOR, - "curatorFee must be greater than or equal to minimum curator fee" - ); - check( - activeBidFee >= FEE_MIN_ACTIVE_BID, - "activeBidFee must be greater than or equal to minimum bid fee" - ); - check( - activeActivationFee >= FEE_MIN_ACTIVE_ACTIVATION, - "activeActivationFee must be greater than or equal to minimum activation fee" - ); }) .pay([ - amt + (constructFee + acceptFee + relayFee + curatorFee) + SERIAL_VER, + amt + (FEE_MIN_CONSTRUCT + FEE_MIN_ACCEPT + FEE_MIN_RELAY) + SERIAL_VER, [tokenAmount, token], - [activeActivationFee, activeToken], + [FEE_MIN_ACTIVE_ACTIVATION, activeToken], ]) .timeout(relativeTime(ttl), () => { // Step @@ -255,8 +186,8 @@ export const App = (map) => { exit(); }); transfer([ - amt + constructFee + SERIAL_VER, - [activeActivationFee, activeToken], + amt + FEE_MIN_CONSTRUCT + SERIAL_VER, + [FEE_MIN_ACTIVE_ACTIVATION, activeToken], ]).to(addr); Auctioneer.interact.signal(); @@ -285,19 +216,10 @@ export const App = (map) => { distr, royaltyCap: royaltyCap, who: Auctioneer, - partTake: 0, - acceptFee, - constructFee, - relayFee, - curatorFee, - curatorAddr: Auctioneer, timestamp: referenceConcensusSecs, activeToken, activeAmount: 0, - activeAddr: Auctioneer, activeCtc: getContract(), // ref to self never used - activeBidFee, - activeActivationFee, }; // Step @@ -318,7 +240,7 @@ export const App = (map) => { .invariant(balance(activeToken) == 0, "active token balance accurate") // BALANCE .invariant( - implies(!state.closed, balance() == acceptFee + relayFee + curatorFee), + implies(!state.closed, balance() == FEE_MIN_ACCEPT + FEE_MIN_RELAY), "balance accurate before close" ) // REM missing invariant balance accurate after close @@ -377,8 +299,7 @@ export const App = (map) => { const sellerTake = remaining - proceedTake; transfer(cent).to(addr); transfer(sellerTake).to(Auctioneer); - transfer([acceptFee + diff, [tokenAmount, token]]).to(this); - transfer(curatorFee).to(cAddr); + transfer([FEE_MIN_ACCEPT + diff, [tokenAmount, token]]).to(this); switch (mctc) { case Some: rUnstake(mctc); @@ -387,11 +308,11 @@ export const App = (map) => { return [ { ...state, + addrs: Array.set(state.addrs, ADDR_RESERVED_CURATOR, cAddr), + distr: distr.map((d) => d * partTake), currentPrice: bal, who: this, closed: true, - curatorAddr: cAddr, - partTake, }, mctc, ]; @@ -407,7 +328,7 @@ export const App = (map) => { return [ (k) => { k(null); - transfer([acceptFee + curatorFee, [tokenAmount, token]]).to(this); + transfer([FEE_MIN_ACCEPT, [tokenAmount, token]]).to(this); switch (mctc) { case Some: rUnstake(mctc); @@ -416,9 +337,14 @@ export const App = (map) => { return [ { ...state, + addrs: Array.set( + state.addrs, + ADDR_RESERVED_ACTIVE_BIDDER, + Auctioneer + ), closed: true, activeAmount: 0, - activeAddr: Auctioneer, + activeCtc: getContract(), }, MContract.None(), ]; @@ -431,10 +357,10 @@ export const App = (map) => { // unlock active token if any .api_(a.bid, (ctc) => { return [ - [0, [activeBidFee, activeToken]], + [0, [FEE_MIN_ACTIVE_BID, activeToken]], (k) => { k(null); - transfer([0, [activeBidFee, activeToken]]).to(addr); + transfer([0, [FEE_MIN_ACTIVE_BID, activeToken]]).to(addr); const { manager: r1Manager, tokenAmount: r1TokenAmount } = rStake( ctc, activeToken, @@ -448,8 +374,18 @@ export const App = (map) => { return [ { ...state, + currentPrice: priceFunc(thisConsensusSecs())( + startPrice, + floorPrice, + referenceConcensusSecs, + dk + ), + addrs: Array.set( + state.addrs, + ADDR_RESERVED_ACTIVE_BIDDER, + r1Manager + ), activeAmount: r1TokenAmount, - activeAddr: r1Manager, activeCtc: ctc, }, MContract.Some(ctc), @@ -461,7 +397,7 @@ export const App = (map) => { // allows the bidder to cancel their bid // unstakes active token if any .api_(a.bidCancel, () => { - check(this == state.activeAddr, "only active bidder can cancel bid"); + check(this == state.addrs[ADDR_RESERVED_ACTIVE_BIDDER], "only active bidder can cancel bid"); return [ (k) => { k(null); @@ -473,8 +409,19 @@ export const App = (map) => { return [ { ...state, + currentPrice: priceFunc(thisConsensusSecs())( + startPrice, + floorPrice, + referenceConcensusSecs, + dk + ), + addrs: Array.set( + state.addrs, + ADDR_RESERVED_ACTIVE_BIDDER, + Auctioneer + ), activeAmount: 0, - activeAddr: Auctioneer, + activeCtc: getContract(), }, MContract.None(), ]; @@ -486,8 +433,8 @@ export const App = (map) => { Relay.publish(); ((recvAmount, pDistr) => { - transfer(pDistr[0]).to(state.activeAddr); // reserved - transfer(pDistr[1]).to(addrs[1]); + transfer(pDistr[0]).to(state.addrs[0]); // reserved for active bidder + transfer(pDistr[1]).to(state.addrs[1]); // reserved for curator transfer(pDistr[2]).to(addrs[2]); transfer(pDistr[3]).to(addrs[3]); commit(); @@ -511,11 +458,12 @@ export const App = (map) => { [getUntrackedFunds(token), token], [getUntrackedFunds(activeToken), activeToken], ]).to(rAddr); + transfer(pDistr[8]).to(addrs[8]); commit(); exit(); })( - balance() - distrTake * state.partTake, - distr.map((d) => d * state.partTake) + balance() - state.distr.sum(), + state.distr ); }; // ----------------------------------------------- From 7092892bad3707f03a03c982aff71434527a79c0 Mon Sep 17 00:00:00 2001 From: Nicholas Shellabarger Date: Wed, 9 Nov 2022 23:04:03 -0700 Subject: [PATCH 6/6] update stake --- interface.rsh | 231 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 140 insertions(+), 91 deletions(-) diff --git a/interface.rsh b/interface.rsh index 7689588..380dc00 100644 --- a/interface.rsh +++ b/interface.rsh @@ -3,7 +3,7 @@ // ----------------------------------------------- // Name: KINN Active Reverse Auction (A1) -// Version: 1.2.7 - protect relay txn from rt +// Version: 1.2.8 - updat stake add delegate // Requires Reach v0.1.11-rc7 (27cb9643) or later // ----------------------------------------------- // TODO calculate price change per second with more precision @@ -12,24 +12,38 @@ import { min, max } from "@nash-protocol/starter-kit#lite-v0.1.9r1:util.rsh"; -import { rStake, rUnstake } from "@KinnFoundation/stake#stake-v0.1.11r0:interface.rsh"; +import { + view, + baseEvents, + baseState +} from "@KinnFoundation/base#base-v0.1.11r4:interface.rsh"; -import { Params, State as ReverseState, MContract } from "@KinnFoundation/reverse#reverse-v0.1.11r3:interface.rsh"; +import { + rStake, + rUnstake +} from "@KinnFoundation/stake#stake-v0.1.11r1:interface.rsh"; + +import { + Params, + State as ReverseState, + MContract +} from "@KinnFoundation/reverse#reverse-v0.1.11r3:interface.rsh"; // CONSTS const SERIAL_VER = 0; // serial version of reach app reserved to release identical contracts under a separate plana id -const DIST_LENGTH = 9; // number of slots to distribute proceeds after sale +const DIST_LENGTH = 10; // number of slots to distribute proceeds after sale -const FEE_MIN_ACCEPT = 8_000; // 0.008 -const FEE_MIN_CONSTRUCT = 7_000; // 0.007 -const FEE_MIN_RELAY = 20_000; // 0.019 + 0.001 const FEE_MIN_ACTIVE_BID = 1; // 1au const FEE_MIN_ACTIVE_ACTIVATION = 1; // 1au +/* + * namedd indices for addrs + */ const ADDR_RESERVED_ACTIVE_BIDDER = 0; const ADDR_RESERVED_CURATOR = 1; +const ADDR_RESERVED_ADDR = 2; // TYPES @@ -41,9 +55,45 @@ const ActiveState = Struct([ const State = Struct([ ...Struct.fields(ReverseState(DIST_LENGTH)), - ...Struct.fields(ActiveState) + ...Struct.fields(ActiveState), ]); +// FUN + +const fState = (State) => Fun([], State); +export const fTouch = Fun([], Null); +export const fAcceptOffer = Fun([Address], Null); +export const fCancel = Fun([], Null); +export const fBid = Fun([Contract], Null); +export const fBidCancel = Fun([], Null); + +// REMOTE FUN + +export const rState = (ctc, State) => { + const r = remote(ctc, { state: fState(State) }); + return r.state(); +}; + +export const rTouch = (ctc) => { + const r = remote(ctc, { touch: fTouch }); + r.touch(); +}; + +export const rBid = (ctc) => { + const r = remote(ctc, { bid: fBid }); + r.bid(); +}; + +// API + +export const api = { + touch: fTouch, + acceptOffer: fAcceptOffer, + cancel: fCancel, + bid: fBid, + bidCancel: fBidCancel, +}; + // FUNCS /* @@ -96,46 +146,36 @@ const safePercent = (amount, percentage, percentPrecision) => const auctioneerInteract = { getParams: Fun([], Params(DIST_LENGTH)), - signal: Fun([], Null), }; const relayInteract = {}; +const eveInteract = {}; + // CONTRACT -export const Event = () => []; +export const Event = () => [Events({ ...baseEvents })]; export const Participants = () => [ - Participant("Auctioneer", auctioneerInteract), - ParticipantClass("Relay", relayInteract), + Participant("Manager", auctioneerInteract), + Participant("Relay", relayInteract), + Participant("Eve", eveInteract), ]; -export const Views = () => [ - View({ - state: State, - }), -]; +export const Views = () => [View(view(State))]; -export const Api = () => [ - API({ - touch: Fun([], Null), - acceptOffer: Fun([Address], Null), - cancel: Fun([], Null), - bid: Fun([Contract], Null), - bidCancel: Fun([], Null), - }), -]; +export const Api = () => [API(api)]; export const App = (map) => { const [ { amt, ttl, tok0: token, tok1: activeToken }, [addr, _], - [Auctioneer, Relay], + [Manager, Relay, _], [v], [a], - _, + [e], ] = map; - Auctioneer.only(() => { + Manager.only(() => { const { tokenAmount, startPrice, @@ -146,9 +186,8 @@ export const App = (map) => { royaltyCap, } = declassify(interact.getParams()); }); - // Step - Auctioneer.publish( + Manager.publish( tokenAmount, startPrice, floorPrice, @@ -175,7 +214,7 @@ export const App = (map) => { ); }) .pay([ - amt + (FEE_MIN_CONSTRUCT + FEE_MIN_ACCEPT + FEE_MIN_RELAY) + SERIAL_VER, + amt + SERIAL_VER, [tokenAmount, token], [FEE_MIN_ACTIVE_ACTIVATION, activeToken], ]) @@ -185,12 +224,11 @@ export const App = (map) => { commit(); exit(); }); - transfer([ - amt + FEE_MIN_CONSTRUCT + SERIAL_VER, - [FEE_MIN_ACTIVE_ACTIVATION, activeToken], - ]).to(addr); + transfer([amt + SERIAL_VER, [FEE_MIN_ACTIVE_ACTIVATION, activeToken]]).to( + addr + ); - Auctioneer.interact.signal(); + e.appLaunch(); const distrTake = distr.sum(); @@ -203,19 +241,18 @@ export const App = (map) => { ).i.i; const initialState = { - manager: Auctioneer, + ...baseState(Manager), token, tokenAmount, currentPrice: startPrice, startPrice, floorPrice, - closed: false, endSecs, priceChangePerSec: dk / precision, - addrs, + addrs: Array.set(addrs, ADDR_RESERVED_ADDR, addr), distr, royaltyCap: royaltyCap, - who: Auctioneer, + who: Manager, timestamp: referenceConcensusSecs, activeToken, activeAmount: 0, @@ -240,10 +277,16 @@ export const App = (map) => { .invariant(balance(activeToken) == 0, "active token balance accurate") // BALANCE .invariant( - implies(!state.closed, balance() == FEE_MIN_ACCEPT + FEE_MIN_RELAY), + implies(!state.closed, balance() == 0), "balance accurate before close" ) - // REM missing invariant balance accurate after close + .invariant( + implies( + state.closed, + balance() == state.distr.slice(2, DIST_LENGTH - 2).sum() + ), + "balance accurate after close" + ) .while(!state.closed) .paySpec([activeToken]) // api: updates current price @@ -280,6 +323,11 @@ export const App = (map) => { // transfers currator fee to curator .api_(a.acceptOffer, (cAddr) => { check(cAddr != this, "cannot accept offer as curator"); + check( + state.addrs[ADDR_RESERVED_ACTIVE_BIDDER] != this, + "cannot accept offer as bidder" + ); + check(Manager != this, "cannot accept offer as manager"); return [ [state.currentPrice, [0, activeToken]], (k) => { @@ -292,14 +340,15 @@ export const App = (map) => { ); // expect state[cp] >= bal const diff = state.currentPrice - bal; - const cent = bal / 100; - const remaining = bal - cent; - const partTake = remaining / royaltyCap; + const partTake = bal / royaltyCap; const proceedTake = partTake * distrTake; - const sellerTake = remaining - proceedTake; - transfer(cent).to(addr); - transfer(sellerTake).to(Auctioneer); - transfer([FEE_MIN_ACCEPT + diff, [tokenAmount, token]]).to(this); + const sellerTake = bal - proceedTake; + transfer(distr[0] * partTake).to( + state.addrs[ADDR_RESERVED_ACTIVE_BIDDER] + ); + transfer(distr[1] * partTake).to(state.addrs[ADDR_RESERVED_CURATOR]); + transfer(sellerTake).to(Manager); + transfer([diff, [tokenAmount, token]]).to(this); switch (mctc) { case Some: rUnstake(mctc); @@ -309,7 +358,15 @@ export const App = (map) => { { ...state, addrs: Array.set(state.addrs, ADDR_RESERVED_CURATOR, cAddr), - distr: distr.map((d) => d * partTake), + distr: Array.set( + Array.set( + distr.map((d) => d * partTake), + ADDR_RESERVED_ACTIVE_BIDDER, + 0 + ), + ADDR_RESERVED_CURATOR, + 0 + ), currentPrice: bal, who: this, closed: true, @@ -324,11 +381,11 @@ export const App = (map) => { // transfers accept and curator fee and token(s) back to auctionee // unstakes active token if any .api_(a.cancel, () => { - check(this == Auctioneer, "only auctioneer can cancel"); + check(this == Manager, "only auctioneer can cancel"); return [ (k) => { k(null); - transfer([FEE_MIN_ACCEPT, [tokenAmount, token]]).to(this); + transfer([[tokenAmount, token]]).to(this); switch (mctc) { case Some: rUnstake(mctc); @@ -337,12 +394,9 @@ export const App = (map) => { return [ { ...state, - addrs: Array.set( - state.addrs, - ADDR_RESERVED_ACTIVE_BIDDER, - Auctioneer - ), + distr: Array.replicate(DIST_LENGTH, 0), closed: true, + currentPrice: 0, activeAmount: 0, activeCtc: getContract(), }, @@ -397,7 +451,10 @@ export const App = (map) => { // allows the bidder to cancel their bid // unstakes active token if any .api_(a.bidCancel, () => { - check(this == state.addrs[ADDR_RESERVED_ACTIVE_BIDDER], "only active bidder can cancel bid"); + check( + this == state.addrs[ADDR_RESERVED_ACTIVE_BIDDER], + "only active bidder can cancel bid" + ); return [ (k) => { k(null); @@ -418,7 +475,7 @@ export const App = (map) => { addrs: Array.set( state.addrs, ADDR_RESERVED_ACTIVE_BIDDER, - Auctioneer + Manager ), activeAmount: 0, activeCtc: getContract(), @@ -429,41 +486,33 @@ export const App = (map) => { ]; }) .timeout(false); + e.appClose(); commit(); + // Step Relay.publish(); - ((recvAmount, pDistr) => { - transfer(pDistr[0]).to(state.addrs[0]); // reserved for active bidder - transfer(pDistr[1]).to(state.addrs[1]); // reserved for curator - transfer(pDistr[2]).to(addrs[2]); - transfer(pDistr[3]).to(addrs[3]); - commit(); - - // Step - Relay.publish(); - transfer(pDistr[4]).to(addrs[4]); - transfer(pDistr[5]).to(addrs[5]); - transfer(pDistr[6]).to(addrs[6]); - transfer(pDistr[7]).to(addrs[7]); - commit(); - - Relay.only(() => { - const rAddr = this; - }); - // Step - Relay.publish(rAddr); - - transfer([ - recvAmount, - [getUntrackedFunds(token), token], - [getUntrackedFunds(activeToken), activeToken], - ]).to(rAddr); - transfer(pDistr[8]).to(addrs[8]); + if ( + state.who == Manager || + state.distr.slice(2, DIST_LENGTH - 2).sum() == 0 + ) { + transfer(state.distr.slice(2, DIST_LENGTH - 2).sum()).to(Manager); commit(); exit(); - })( - balance() - state.distr.sum(), - state.distr - ); + } + transfer(state.distr[2]).to(addrs[2]); + transfer(state.distr[3]).to(addrs[3]); + transfer(state.distr[4]).to(addrs[4]); + transfer(state.distr[5]).to(addrs[5]); + commit(); + // Step + Anybody.publish(); + if (state.distr.slice(6, DIST_LENGTH - 6).sum() != 0) { + transfer(state.distr[6]).to(addrs[6]); + transfer(state.distr[7]).to(addrs[7]); + transfer(state.distr[8]).to(addrs[8]); + transfer(state.distr[9]).to(addrs[9]); + } + commit(); + exit(); }; // -----------------------------------------------