From a0830cba5476f87534629d9ebbdf27777bfe44c6 Mon Sep 17 00:00:00 2001 From: Suzanne Soy Date: Mon, 25 Sep 2023 17:27:09 +0100 Subject: [PATCH 1/6] Modernized syntax for v1.0 --- esy.lock/index.json | 33 ----- {esy.lock => ligo.esy.lock}/.gitattributes | 0 {esy.lock => ligo.esy.lock}/.gitignore | 0 ligo.esy.lock/index.json | 29 ++++ package.json => ligo.json | 2 +- src/constants.jsligo | 2 +- src/errors.jsligo | 42 +++--- src/lambda.jsligo | 18 +-- src/main.jsligo | 158 +++++++++++---------- src/outcome.jsligo | 4 +- src/proposal.jsligo | 18 +-- src/storage.jsligo | 12 +- src/timelock.jsligo | 23 ++- src/token.jsligo | 12 +- src/vault.jsligo | 14 +- src/vote.jsligo | 2 +- 16 files changed, 187 insertions(+), 182 deletions(-) delete mode 100644 esy.lock/index.json rename {esy.lock => ligo.esy.lock}/.gitattributes (100%) rename {esy.lock => ligo.esy.lock}/.gitignore (100%) create mode 100644 ligo.esy.lock/index.json rename package.json => ligo.json (96%) diff --git a/esy.lock/index.json b/esy.lock/index.json deleted file mode 100644 index d04703e..0000000 --- a/esy.lock/index.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "checksum": "ce8479008de1b2261bccf6c2f40aef2e", - "root": "@ligo/dao-jsligo@link-dev:./package.json", - "node": { - "@ligo/fa@1.0.1@d41d8cd9": { - "id": "@ligo/fa@1.0.1@d41d8cd9", - "name": "@ligo/fa", - "version": "1.0.1", - "source": { - "type": "install", - "source": [ - "archive:https://packages.ligolang.org/-/api/@ligo/fa/-/@ligo/fa-1.0.1.tgz#sha1:08a730e6eab7b69ce2c3a606dd94e8dd05065759" - ] - }, - "overrides": [], - "dependencies": [], - "devDependencies": [] - }, - "@ligo/dao-jsligo@link-dev:./package.json": { - "id": "@ligo/dao-jsligo@link-dev:./package.json", - "name": "@ligo/dao-jsligo", - "version": "link-dev:./package.json", - "source": { - "type": "link-dev", - "path": ".", - "manifest": "package.json" - }, - "overrides": [], - "dependencies": [ "@ligo/fa@1.0.1@d41d8cd9" ], - "devDependencies": [] - } - } -} \ No newline at end of file diff --git a/esy.lock/.gitattributes b/ligo.esy.lock/.gitattributes similarity index 100% rename from esy.lock/.gitattributes rename to ligo.esy.lock/.gitattributes diff --git a/esy.lock/.gitignore b/ligo.esy.lock/.gitignore similarity index 100% rename from esy.lock/.gitignore rename to ligo.esy.lock/.gitignore diff --git a/ligo.esy.lock/index.json b/ligo.esy.lock/index.json new file mode 100644 index 0000000..f94126d --- /dev/null +++ b/ligo.esy.lock/index.json @@ -0,0 +1,29 @@ +{ + "checksum": "e92ce686850cf2560180a590d07494d3", + "root": "@ligo/dao-jsligo@link-dev:./ligo.json", + "node": { + "@ligo/fa@1.0.6@d41d8cd9": { + "id": "@ligo/fa@1.0.6@d41d8cd9", + "name": "@ligo/fa", + "version": "1.0.6", + "source": { + "type": "install", + "source": [ + "archive:https://packages.ligolang.org/-/api/@ligo/fa/-/@ligo/fa-1.0.6.tgz#sha1:94927de94317e20b350e51ecfa90a647c322e665" + ] + }, + "overrides": [], + "dependencies": [], + "devDependencies": [] + }, + "@ligo/dao-jsligo@link-dev:./ligo.json": { + "id": "@ligo/dao-jsligo@link-dev:./ligo.json", + "name": "@ligo/dao-jsligo", + "version": "link-dev:./ligo.json", + "source": { "type": "link-dev", "path": ".", "manifest": "ligo.json" }, + "overrides": [], + "dependencies": [ "@ligo/fa@1.0.6@d41d8cd9" ], + "devDependencies": [] + } + } +} \ No newline at end of file diff --git a/package.json b/ligo.json similarity index 96% rename from package.json rename to ligo.json index 4000e56..dc7ffb7 100644 --- a/package.json +++ b/ligo.json @@ -13,7 +13,7 @@ "author": "ligoLANG ", "main": "src/main.jsligo", "dependencies": { - "@ligo/fa": "1.0.1" + "@ligo/fa": "1.0.6" }, "bugs": { "url": "https://github.com/ligolang/dao-jslgo/issues" diff --git a/src/constants.jsligo b/src/constants.jsligo index 254f2a2..7b895e6 100644 --- a/src/constants.jsligo +++ b/src/constants.jsligo @@ -1 +1 @@ -const noOperation : list = list([]) +export const noOperation : list = list([]) diff --git a/src/errors.jsligo b/src/errors.jsligo index 90a23f8..548d4e3 100644 --- a/src/errors.jsligo +++ b/src/errors.jsligo @@ -1,21 +1,21 @@ -const proposalAlreadyExists = "PROPOSAL_ALREADY_EXISTS" -const votingPeriod = "VOTING_PERIOD" -const notVotingPeriod = "NOT_VOTING_PERIOD" -const noProposal = "NO_PROPOSAL" -const notCreator = "NOT_CREATOR" -const canceled = "CANCELED" -const timelockNotFound = "TIMELOCK_NOT_FOUND" -const timelockLocked = "TIMELOCK_LOCKED" -const timelockUnlocked = "TIMELOCK_UNLOCKED" -const notZeroAmount = "NOT_ZERO_AMOUNT" -const noLockedTokens = "NO_LOCKED_TOKENS" -const notEnoughBalance = "NOT_ENOUGH_BALANCE" -const receiverNotFound = "RECEIVER_NOT_FOUND" -const fa2TotalSupplyNotFound = "FA2_TOTAL_SUPPLY_NOT_FOUND" -const outcomeNotFound = "OUTCOME_NOT_FOUND" -const alreadyExecuted = "ALREADY_EXECUTED" -const notExecutable = "NOT_EXECUTABLE" -const hashNotFound = "HASH_NOT_FOUND" -const hashNotMatch = "HASH_NOT_MATCH" -const unpackMismatch = "UNPACK_MISMATCH" -const nothingToCancel = "NOTHING_TO_CANCEL" +export const proposalAlreadyExists = "PROPOSAL_ALREADY_EXISTS" +export const votingPeriod = "VOTING_PERIOD" +export const notVotingPeriod = "NOT_VOTING_PERIOD" +export const noProposal = "NO_PROPOSAL" +export const notCreator = "NOT_CREATOR" +export const canceled = "CANCELED" +export const timelockNotFound = "TIMELOCK_NOT_FOUND" +export const timelockLocked = "TIMELOCK_LOCKED" +export const timelockUnlocked = "TIMELOCK_UNLOCKED" +export const notZeroAmount = "NOT_ZERO_AMOUNT" +export const noLockedTokens = "NO_LOCKED_TOKENS" +export const notEnoughBalance = "NOT_ENOUGH_BALANCE" +export const receiverNotFound = "RECEIVER_NOT_FOUND" +export const fa2TotalSupplyNotFound = "FA2_TOTAL_SUPPLY_NOT_FOUND" +export const outcomeNotFound = "OUTCOME_NOT_FOUND" +export const alreadyExecuted = "ALREADY_EXECUTED" +export const notExecutable = "NOT_EXECUTABLE" +export const hashNotFound = "HASH_NOT_FOUND" +export const hashNotMatch = "HASH_NOT_MATCH" +export const unpackMismatch = "UNPACK_MISMATCH" +export const nothingToCancel = "NOTHING_TO_CANCEL" diff --git a/src/lambda.jsligo b/src/lambda.jsligo index d3691b4..763b73b 100644 --- a/src/lambda.jsligo +++ b/src/lambda.jsligo @@ -8,19 +8,19 @@ export type t = | ["ParameterChange", parameterChange] | ["OperationList", operationList]; -const unpack = (hashOpt: option, packed: bytes): t => { - return match(hashOpt, { - None: () => failwith(Errors.hashNotFound), - Some: (hash_: bytes) => { +export const unpack = (hashOpt: option, packed: bytes): t => { + return match(hashOpt) { + when(None): failwith(Errors.hashNotFound); + when(Some(hash_)): do { assert_with_error( hash_ == Crypto.sha256(packed), Errors.hashNotMatch ); - return match((Bytes.unpack(packed) as option), { - None: () => failwith(Errors.unpackMismatch), - Some: (lambda_: t) => lambda_ - }); + return match(Bytes.unpack(packed) as option) { + when(None): failwith(Errors.unpackMismatch); + when(Some(lambda_)): lambda_ + }; } - }); + }; }; diff --git a/src/main.jsligo b/src/main.jsligo index 0222a34..023a8a8 100644 --- a/src/main.jsligo +++ b/src/main.jsligo @@ -24,33 +24,34 @@ export type storage = Storage.t; type result = [list, storage]; const execute = (outcomeKey: nat, packed: bytes, s: storage) : result => { - const proposal = match(Big_map.find_opt(outcomeKey, s.outcomes), { - None: () => failwith(Errors.outcomeNotFound), - Some: (o: Outcome.t) => Outcome.getExecutableProposal(o) - }); + const proposal = match(Big_map.find_opt(outcomeKey, s.outcomes)) { + when(None): failwith(Errors.outcomeNotFound); + when(Some(outcome)): Outcome.getExecutableProposal(outcome) + }; Timelock._checkUnlocked(proposal.timelock); const lambda_ : Lambda.t = Lambda.unpack(proposal.hash, packed); - return match(lambda_, { - OperationList: (f: Lambda.operationList) => [ + return match(lambda_) { + when(OperationList(f)): [ f(), Storage.updateOutcome(outcomeKey, [proposal, Executed()], s) - ], - ParameterChange: (f: Lambda.parameterChange) => [ + ]; + when(ParameterChange(f)): [ Constants.noOperation, Storage.updateOutcome( outcomeKey, [proposal, Executed()], Storage.updateConfig(f,s) - )] - }); + ) + ] + }; }; const propose = (p: Proposal.makeParams, s: storage) : result => - match(s.proposal, { - Some: (_: Proposal.t) => failwith(Errors.proposalAlreadyExists), - None: () => [ + match(s.proposal) { + when(Some(_proposal)): failwith(Errors.proposalAlreadyExists); + when(None): [ list([Token.transfer( [s.governanceToken, Tezos.get_sender(), @@ -60,43 +61,49 @@ const propose = (p: Proposal.makeParams, s: storage) : result => Proposal.make(p, s.config.startDelay, s.config.votingPeriod), s) ] - }); + }; const cancel = (outcomeKeyOpt: option, s: storage) : result => - [list([Token.transfer( - [s.governanceToken, - Tezos.get_self_address(), - s.config.burnAddress, - s.config.depositAmount]) - ]), match(outcomeKeyOpt, { - None: () => match(s.proposal, { - None: () => failwith(Errors.nothingToCancel), - Some: (p: Proposal.t) => { - Proposal._checkNotVotingPeriod(p); - assert_with_error( - p.creator == Tezos.get_sender(), - Errors.notCreator - ); - - return Storage.addOutcome([p, Canceled()], s); - }}), - Some: (outcomeKey: nat) => match(Big_map.find_opt(outcomeKey, s.outcomes), { - None: () => failwith(Errors.outcomeNotFound), - Some: (o: Outcome.t) => { - const [p, state] = o; - assert_with_error( - p.creator == Tezos.get_sender(), - Errors.notCreator - ); - assert_with_error( - state != (Executed() as Outcome.state), - Errors.alreadyExecuted - ); - Timelock._checkLocked(p.timelock); - - return Storage.updateOutcome(outcomeKey, [p, Canceled()], s) - }}) - }) + [ + list([Token.transfer( + [s.governanceToken, + Tezos.get_self_address(), + s.config.burnAddress, + s.config.depositAmount]) + ]), + match(outcomeKeyOpt) { + when(None): + match(s.proposal) { + when(None): failwith(Errors.nothingToCancel); + when(Some(proposal)): do { + Proposal._checkNotVotingPeriod(proposal); + assert_with_error( + proposal.creator == Tezos.get_sender(), + Errors.notCreator + ); + + return Storage.addOutcome([proposal, Canceled()], s); + } + }; + when(Some(outcomeKey)): + match(Big_map.find_opt(outcomeKey, s.outcomes)) { + when(None): failwith(Errors.outcomeNotFound); + when(Some(outcome)): do { + const [p, state] = outcome; + assert_with_error( + p.creator == Tezos.get_sender(), + Errors.notCreator + ); + assert_with_error( + state != (Executed() as Outcome.state), + Errors.alreadyExecuted + ); + Timelock._checkLocked(p.timelock); + + return Storage.updateOutcome(outcomeKey, [p, Canceled()], s) + } + } + } ]; const lock = (amount_: nat, s: storage) : result => { @@ -133,25 +140,26 @@ const release = (amount_: nat, s: storage) : result => { }; const vote = (choice: bool, s: storage) : storage => - match(s.proposal, { - None: () => failwith(Errors.noProposal), - Some: (p: Proposal.t) => { - Proposal._checkIsVotingPeriod(p); + match(s.proposal) { + when(None): failwith(Errors.noProposal); + when(Some(proposal)): do { + Proposal._checkIsVotingPeriod(proposal); const amount_ = Vault.getForUserExn([s.vault, Tezos.get_sender()]); - return Storage.updateVotes(p, [choice, amount_], s); - }}); + return Storage.updateVotes(proposal, [choice, amount_], s); + } + }; const endVote = (s: storage) : result => - match(s.proposal,{ - None: () => failwith(Errors.noProposal), - Some: (p: Proposal.t) => { - Proposal._checkVotingPeriodEnded(p); - const totalSupply = match(Token.getTotalSupply(s.governanceToken),{ - None: () => failwith(Errors.fa2TotalSupplyNotFound), - Some: (n: nat) => n - }); + match(s.proposal) { + when(None): failwith(Errors.noProposal); + when(Some(proposal)): do { + Proposal._checkVotingPeriodEnded(proposal); + const totalSupply = match(Token.getTotalSupply(s.governanceToken)) { + when(None): failwith(Errors.fa2TotalSupplyNotFound); + when(Some(n)): n + }; const outcome = Outcome.make( - p, + proposal, totalSupply, s.config.refundThreshold, s.config.quorumThreshold, @@ -159,7 +167,7 @@ const endVote = (s: storage) : result => ); const [_, state] = outcome; - let transferToAddr = p.creator; + let transferToAddr = proposal.creator; if (Rejected_(WithoutRefund()) == state) { transferToAddr = s.config.burnAddress; } @@ -171,17 +179,19 @@ const endVote = (s: storage) : result => s.config.depositAmount])]) , Storage.addOutcome(outcome, s) ]; - }}); + } + }; +@entry const main = (action: parameter, store: storage) : result => { assert_with_error(Tezos.get_amount() == (0 as tez), Errors.notZeroAmount); - return match(action, { - Propose: (p: Proposal.makeParams) => propose(p, store), - Cancel: (nOpt: option) => cancel(nOpt, store), - Lock: (n: nat) => lock(n, store), - Release: (n: nat) => release(n, store), - Execute: (p: Outcome.executeParams) => execute(p.outcomeKey, p.packed, store), - Vote: (v: Vote.choice) => [Constants.noOperation, vote(v, store)], - EndVote: () => endVote(store) - }); + return match(action) { + when(Propose(proposalMakeParams)): propose(proposalMakeParams, store); + when(Cancel(nOpt)): cancel(nOpt, store); + when(Lock(n)): lock(n, store); + when(Release(n)): release(n, store); + when(Execute(outcomeExecuteParam)): execute(outcomeExecuteParam.outcomeKey, outcomeExecuteParam.packed, store); + when(Vote(voteChoice)): [Constants.noOperation, vote(voteChoice, store)]; + when(EndVote): endVote(store); + }; }; diff --git a/src/outcome.jsligo b/src/outcome.jsligo index 9f25c9a..23eef55 100644 --- a/src/outcome.jsligo +++ b/src/outcome.jsligo @@ -41,7 +41,7 @@ const makeState = ( } } -const make = (p: Proposal.t, +export const make = (p: Proposal.t, totalSupply: nat, refundThreshold: nat, quorumThreshold: nat, @@ -57,7 +57,7 @@ const make = (p: Proposal.t, ) ]; -const getExecutableProposal = (outcome: t) : Proposal.t => { +export const getExecutableProposal = (outcome: t) : Proposal.t => { const [p, state] = outcome; if ((Accepted() as state) == state) { diff --git a/src/proposal.jsligo b/src/proposal.jsligo index 0d854e6..d0cb487 100644 --- a/src/proposal.jsligo +++ b/src/proposal.jsligo @@ -21,7 +21,7 @@ export type makeParams = hash: option } -const make = (p: makeParams, startDelay: nat, votingPeriod: nat) : t => { +export const make = (p: makeParams, startDelay: nat, votingPeriod: nat) : t => { const startAt = Tezos.get_now() + int(startDelay); return { @@ -38,17 +38,17 @@ const make = (p: makeParams, startDelay: nat, votingPeriod: nat) : t => { const isVotingPeriod = (p: t) : bool => ((Tezos.get_now() >= p.startAt) && (Tezos.get_now() < p.endAt)); -const _checkNotVotingPeriod = (p: t) : unit => +export const _checkNotVotingPeriod = (p: t) : unit => assert_with_error(!isVotingPeriod(p), Errors.votingPeriod); -const _checkIsVotingPeriod = (p: t) : unit => +export const _checkIsVotingPeriod = (p: t) : unit => assert_with_error(isVotingPeriod(p), Errors.notVotingPeriod); -const _checkNoVoteOngoing = (p_opt: option) : unit => - match(p_opt, { - Some: (p: t) => _checkNotVotingPeriod(p), - None: () => unit - }); +export const _checkNoVoteOngoing = (p_opt: option) : unit => + match(p_opt) { + when(Some(p)): _checkNotVotingPeriod(p); + when(None): unit; + }; -const _checkVotingPeriodEnded = (p: t) : unit => +export const _checkVotingPeriodEnded = (p: t) : unit => assert_with_error(Tezos.get_now() > p.endAt, Errors.votingPeriod); diff --git a/src/storage.jsligo b/src/storage.jsligo index f4d4df0..1a9b3ac 100644 --- a/src/storage.jsligo +++ b/src/storage.jsligo @@ -22,25 +22,25 @@ export type t = nextOutcomeId: nat } -const createProposal = (p: Proposal.t, s: t) : t => +export const createProposal = (p: Proposal.t, s: t) : t => ({ ...s, proposal: Some(p) }) -const updateConfig = (f: Lambda.parameterChange, s: t) : t => +export const updateConfig = (f: Lambda.parameterChange, s: t) : t => ({ ...s, config: f() }) -const updateVault = (v: Vault.t, s: t) : t => +export const updateVault = (v: Vault.t, s: t) : t => ({ ...s, vault: v }) -const updateVotes = (p: Proposal.t, v: Vote.t, s: t) : t => { +export const updateVotes = (p: Proposal.t, v: Vote.t, s: t) : t => { const newVotes = Map.update(Tezos.get_sender(), Some(v), p.votes); const newProposal = { ...p, votes: newVotes }; return { ...s, proposal: Some(newProposal) } } -const updateOutcome = (k: nat, o: Outcome.t, s: t) : t => +export const updateOutcome = (k: nat, o: Outcome.t, s: t) : t => ({ ...s, outcomes: Big_map.update(k, Some(o), s.outcomes) }) -const addOutcome = (o: Outcome.t, s: t) : t => { +export const addOutcome = (o: Outcome.t, s: t) : t => { let [proposal, status] = o; // If proposal is accepted, also create timelock diff --git a/src/timelock.jsligo b/src/timelock.jsligo index 6963a63..4fd1341 100644 --- a/src/timelock.jsligo +++ b/src/timelock.jsligo @@ -1,7 +1,6 @@ #import "./errors.jsligo" "Errors" export type t = - // @layout:comb { unlock_at: timestamp, // ^ timestamp for the unlock to happen @@ -10,7 +9,7 @@ export type t = // ^ timestamp for the relock to happen }; -const make = (unlock_at: timestamp, timelock_period: nat) : t => ({ +export const make = (unlock_at: timestamp, timelock_period: nat) : t => ({ unlock_at: unlock_at, relock_at: unlock_at + int(timelock_period as nat) }); @@ -18,14 +17,14 @@ const make = (unlock_at: timestamp, timelock_period: nat) : t => ({ const isLocked = (t : t) : bool => ((Tezos.get_now() < t.unlock_at) || (Tezos.get_now() >= t.relock_at)); -const _checkUnlocked = (t_opt : option) : unit => - match(t_opt, { - None: () => failwith(Errors.timelockNotFound), - Some: (t: t) => assert_with_error(!isLocked(t), Errors.timelockLocked) - }); +export const _checkUnlocked = (t_opt : option) : unit => + match(t_opt) { + when(None): failwith(Errors.timelockNotFound); + when(Some(t)): assert_with_error(!isLocked(t), Errors.timelockLocked); + }; -const _checkLocked = (t_opt: option) : unit => - match(t_opt, { - None: () => failwith(Errors.timelockNotFound), - Some: (t: t) => assert_with_error(isLocked(t), Errors.timelockUnlocked) - }); +export const _checkLocked = (t_opt: option) : unit => + match(t_opt) { + when(None): failwith(Errors.timelockNotFound); + when(Some(t)): assert_with_error(isLocked(t), Errors.timelockUnlocked); + }; diff --git a/src/token.jsligo b/src/token.jsligo index f5a8008..5af95a2 100644 --- a/src/token.jsligo +++ b/src/token.jsligo @@ -6,13 +6,13 @@ export type fa2TransferContract = contract; const getTransferEntrypoint = (addr: address) : fa2TransferContract => { const transferOption: option = Tezos.get_entrypoint_opt("%transfer", addr); - return match(transferOption, { - None: () => failwith(Errors.receiverNotFound), - Some: (c: fa2TransferContract) => c - }); + return match(transferOption) { + when(None): failwith(Errors.receiverNotFound); + when(Some(c)): c + }; }; -const transfer = ([token_addr, from_, to_, amount_]: [t, address, address, nat]) : operation => { +export const transfer = ([token_addr, from_, to_, amount_]: [t, address, address, nat]) : operation => { const dest = getTransferEntrypoint(token_addr); const transfer_requests : FA2.transfer = list([ {from_: from_, txs: list([{to_: to_, amount: amount_, token_id: 0 as nat}])} @@ -21,5 +21,5 @@ const transfer = ([token_addr, from_, to_, amount_]: [t, address, address, nat]) return Tezos.transaction(transfer_requests, (0 as tez), dest) }; -const getTotalSupply = (token_addr: t) : option => +export const getTotalSupply = (token_addr: t) : option => Tezos.call_view("total_supply", unit, token_addr) diff --git a/src/vault.jsligo b/src/vault.jsligo index fef3c3a..b89666c 100644 --- a/src/vault.jsligo +++ b/src/vault.jsligo @@ -4,13 +4,13 @@ export type owner = address; export type amount_ = nat; export type t = big_map; -const getForUser = ([vault, owner]: [t, owner]) : amount_ => - match(Big_map.find_opt(owner, vault), { - Some: (tokens: nat) => tokens, - None: () => 0 as nat - }); +export const getForUser = ([vault, owner]: [t, owner]) : amount_ => + match(Big_map.find_opt(owner, vault)) { + when(Some(tokens)): tokens; + when(None): 0 as nat + }; -const getForUserExn = ([vault, owner]: [t, owner]) : amount_ => { +export const getForUserExn = ([vault, owner]: [t, owner]) : amount_ => { const amount_ = getForUser([vault, owner]); if (amount_ == (0 as nat)) { return failwith(Errors.noLockedTokens) @@ -19,5 +19,5 @@ const getForUserExn = ([vault, owner]: [t, owner]) : amount_ => { return amount_ } -const updateForUser = ([vault, owner, amount_]: [t, owner, nat]) : t => +export const updateForUser = ([vault, owner, amount_]: [t, owner, nat]) : t => Big_map.update(owner, Some(amount_ as nat), vault) diff --git a/src/vote.jsligo b/src/vote.jsligo index c3b2e87..6298c2e 100644 --- a/src/vote.jsligo +++ b/src/vote.jsligo @@ -7,7 +7,7 @@ export type votes = map; // - total votes (for + against), // - sum of votes for, // - sum of votes against. -const count = (votes : votes) : [nat, nat, nat] => { +export const count = (votes : votes) : [nat, nat, nat] => { const sum = ([acc, vote]: [[nat,nat], [address, t]]): [nat, nat] => { const [for_, against] = acc; const [_, [choice, nb]] = vote; From e14ea06bde04a97b7a26b96e1959e1a09dced3e1 Mon Sep 17 00:00:00 2001 From: Suzanne Soy Date: Tue, 26 Sep 2023 00:41:08 +0100 Subject: [PATCH 2/6] Started modernizin tests, ran into error --- test/helpers/assert.jsligo | 28 ++++++++++++------------- test/helpers/dao.jsligo | 42 +++++++++++++++++++------------------- test/helpers/token.jsligo | 34 +++++++++++++++--------------- 3 files changed, 52 insertions(+), 52 deletions(-) diff --git a/test/helpers/assert.jsligo b/test/helpers/assert.jsligo index 2f2361f..e9f254e 100644 --- a/test/helpers/assert.jsligo +++ b/test/helpers/assert.jsligo @@ -1,20 +1,20 @@ // Assert contract call results in failwith with given string const stringFailure = (res: test_exec_result,expected: string) : unit => { let expected_bis = Test.eval (expected) ; - match (res, { - Fail: (x: test_exec_error) => ( - match (x, { - Rejected: (x:[michelson_program,address]) => assert (Test.michelson_equal (x[0], expected_bis)), - Balance_too_low: (_: { contract_too_low : address , contract_balance : tez , spend_request : tez }) => Test.failwith ("contract failed for an unknown reason"), - Other: (_:string) => Test.failwith ("contract failed for an unknown reason") - })), - Success: (_:nat) => Test.failwith ("bad price check") - } ); + match (res) { + when(Fail(err)): + match (err) { + when(Rejected([michelson_program,address])): assert (Test.michelson_equal (michelson_program, expected_bis)); + when(Balance_too_low({ contract_too_low, contract_balance , spend_request })): Test.failwith ("contract failed for an unknown reason"); + when(Other(_str)): Test.failwith ("contract failed for an unknown reason"); + }; + when(Success(_nat)): Test.failwith ("bad price check"); + }; } ; // Assert contract result is successful -const txSuccess = (res: test_exec_result) : unit => - match (res, { - Fail: (_: test_exec_error) => Test.failwith("Transaction should not fail"), - Success: (_:nat) => unit - } ); +export const txSuccess = (res: test_exec_result) : unit => + match (res) { + when(Fail(err)): Test.failwith("Transaction should not fail"); + when(Success(_nat)): unit; + }; diff --git a/test/helpers/dao.jsligo b/test/helpers/dao.jsligo index 71cf5d4..f04718f 100644 --- a/test/helpers/dao.jsligo +++ b/test/helpers/dao.jsligo @@ -54,7 +54,7 @@ const baseStorage : DAO.storage = { // Originate a DAO contract with given init_storage storage const originate = (init_storage: DAO.storage) : originated => { - const [taddr, _, _] = Test.originate (DAO.main, init_storage, 0 as tez); + const [taddr, _x, _y] = Test.originate (DAO.main, init_storage, 0 as tez); const contr = Test.to_contract (taddr); const addr = Tezos.address (contr); return { addr: addr, taddr: taddr, contr: contr }; @@ -126,10 +126,10 @@ const batchVote = (addr_lst: list
, choice: DAO.Vote.choice, contr: cont // Assert DAO contract at [taddr] has [owner] [amount_] of tokens locked const assertLocked = (taddr: taddr, owner: DAO.Storage.Vault.owner, amount_: nat) : unit => { const s = Test.get_storage(taddr); - match (Big_map.find_opt(owner, s.vault), { - Some: (tokens: nat) => assert(tokens == amount_), - None: () => Test.failwith("Big_map key should not be missing") - }); + match (Big_map.find_opt(owner, s.vault)) { + when(Some(tokens)): assert(tokens == amount_); + when(None): Test.failwith("Big_map key should not be missing"); + }; }; // Assert DAO contract at [taddr] have registered [voter] [choice] with [amount_] votes @@ -137,34 +137,34 @@ const assertVoted = (taddr: taddr, voter: address, choice: bool, amount_: nat) : unit => { const s = Test.get_storage (taddr); const p = Option.unopt(s.proposal); - match (Map.find_opt(voter, p.votes), { - Some: (vote: DAO.Vote.t) => assert(vote[0] == choice && vote[1] == amount_), - None: () => Test.failwith("Map key should not be missing") - }); + match (Map.find_opt(voter, p.votes)) { + when(Some(vote)): assert(vote[0] == choice && vote[1] == amount_); + when(None): Test.failwith("Map key should not be missing"); + }; }; // Assert DAO contract at [taddr] have an outcome occuring for [n] key in Executed state const assertExecuted = (taddr: taddr, n: nat) : unit => { const s = Test.get_storage (taddr); - match (Big_map.find_opt(n, s.outcomes), { - None: () => Test.failwith("The outcome should exists"), - Some: (o: Outcome.t) => { - const [_, state] = o; - assert(state == (Executed() as Outcome.state)) - } - }); + match (Big_map.find_opt(n, s.outcomes)) { + when(None): Test.failwith("The outcome should exists"); + when(Some(outcome)): do { + const [_, state] = outcome; + assert(state == (Executed() as Outcome.state)); + }; + }; }; // Assert outcomes [bm] big map have an entry for key [k] and has given [s] state const assertProposalState = (bm: DAO.Storage.outcomes, k: nat, s: DAO.Outcome.state) : unit => - match(Big_map.find_opt(k, bm), { - Some: (outcome: DAO.Outcome.t) => { + match(Big_map.find_opt(k, bm)) { + when(Some(outcome)): do { const [proposal, state] = outcome; // just checking timelock existence const _checkTimelock = proposal.timelock; // check that the proposal have been accepted assert(state == s) - }, - None: () => Test.failwith("outcome not found") - }); + }; + when(None): Test.failwith("outcome not found"); + }; diff --git a/test/helpers/token.jsligo b/test/helpers/token.jsligo index 8a4292a..1430370 100644 --- a/test/helpers/token.jsligo +++ b/test/helpers/token.jsligo @@ -20,7 +20,7 @@ const originate = (tok_amount : nat) : originated => { [tok_amount, tok_amount, tok_amount] ); const v_mich = Test.run(((x:SingleAsset.Storage.t) => x), init_storage); - const [addr, _, _] = Test.originate_from_file(f, "main", list(["total_supply"]), v_mich, 0 as tez); + const [addr, _x, _y] = Test.originate_from_file(f, "main", list(["total_supply"]), v_mich, 0 as tez); const taddr : taddr = Test.cast_address (addr); const contr = Test.to_contract (taddr); return { @@ -67,20 +67,20 @@ const assertInsBalanceFailure = (r : test_exec_result) : unit => // assert FA2 contract at [taddr] have [owner] address with [amount_] tokens in its ledger const assertBalanceAmount = (taddr: taddr, owner: SingleAsset.Ledger.owner, amount_: nat) : unit => { const s = Test.get_storage (taddr); - return match (Big_map.find_opt(owner, s.ledger), { - Some: (tokens: nat) => assert(tokens == amount_), - None: () => Test.failwith("Big_map key should not be missing") - }); + return match (Big_map.find_opt(owner, s.ledger)) { + when(Some(tokens)): assert(tokens == amount_); + when(None): Test.failwith("Big_map key should not be missing"); + }; }; // get balance in [taddr] contract for [owner] address const get_balance_for = (taddr: taddr, owner: SingleAsset.Ledger.owner) : nat => { const s = Test.get_storage (taddr); - return match (Big_map.find_opt(owner, s.ledger), { - Some: (amount_: nat) => amount_, - None: () => 0 as nat - }); + return match (Big_map.find_opt(owner, s.ledger)) { + when(Some(amount_)): amount_; + when(None): 0 as nat; + }; }; // Create a lambda of type (list) that would call the "transfer" @@ -88,20 +88,20 @@ nat => { // [from_] and [to_] addresses with [amount_] tokens. const createTransferCallable = (addr: address, from_: address, to_: address, amount_: nat) : DAO.Lambda.operationList => { - return () : list => { + return (() : list => { const transferOpt: option> = Tezos.get_entrypoint_opt("%transfer", addr); - return match (transferOpt, { - Some: (c: contract) => { + return match (transferOpt) { + when(Some(contr)): do { const transfer_requests: SingleAsset.transfer = list([ {from_: from_, txs: list([{to_: to_, amount: amount_, token_id: 0 as nat}]) as list } ]); - const op = Tezos.transaction(transfer_requests, 0 as tez, c); + const op = Tezos.transaction(transfer_requests, 0 as tez, contr); return list([op]); - }, - None: () => failwith("TOKEN_CONTRACT_NOT_FOUND") - }); - } + }; + when(None): failwith("TOKEN_CONTRACT_NOT_FOUND"); + }; + }); }; From c7daf1f6320d15ce8e598a8fe0893f8844d9a0f9 Mon Sep 17 00:00:00 2001 From: Suzanne Soy Date: Wed, 27 Sep 2023 12:56:22 +0100 Subject: [PATCH 3/6] drop ligo.esy.lock --- ligo.esy.lock/.gitattributes | 3 --- ligo.esy.lock/.gitignore | 3 --- ligo.esy.lock/index.json | 29 ----------------------------- 3 files changed, 35 deletions(-) delete mode 100644 ligo.esy.lock/.gitattributes delete mode 100644 ligo.esy.lock/.gitignore delete mode 100644 ligo.esy.lock/index.json diff --git a/ligo.esy.lock/.gitattributes b/ligo.esy.lock/.gitattributes deleted file mode 100644 index e0b4e26..0000000 --- a/ligo.esy.lock/.gitattributes +++ /dev/null @@ -1,3 +0,0 @@ - -# Set eol to LF so files aren't converted to CRLF-eol on Windows. -* text eol=lf linguist-generated diff --git a/ligo.esy.lock/.gitignore b/ligo.esy.lock/.gitignore deleted file mode 100644 index a221be2..0000000 --- a/ligo.esy.lock/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ - -# Reset any possible .gitignore, we want all esy.lock to be un-ignored. -!* diff --git a/ligo.esy.lock/index.json b/ligo.esy.lock/index.json deleted file mode 100644 index f94126d..0000000 --- a/ligo.esy.lock/index.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "checksum": "e92ce686850cf2560180a590d07494d3", - "root": "@ligo/dao-jsligo@link-dev:./ligo.json", - "node": { - "@ligo/fa@1.0.6@d41d8cd9": { - "id": "@ligo/fa@1.0.6@d41d8cd9", - "name": "@ligo/fa", - "version": "1.0.6", - "source": { - "type": "install", - "source": [ - "archive:https://packages.ligolang.org/-/api/@ligo/fa/-/@ligo/fa-1.0.6.tgz#sha1:94927de94317e20b350e51ecfa90a647c322e665" - ] - }, - "overrides": [], - "dependencies": [], - "devDependencies": [] - }, - "@ligo/dao-jsligo@link-dev:./ligo.json": { - "id": "@ligo/dao-jsligo@link-dev:./ligo.json", - "name": "@ligo/dao-jsligo", - "version": "link-dev:./ligo.json", - "source": { "type": "link-dev", "path": ".", "manifest": "ligo.json" }, - "overrides": [], - "dependencies": [ "@ligo/fa@1.0.6@d41d8cd9" ], - "devDependencies": [] - } - } -} \ No newline at end of file From e99bc6bb8f47a237c819dde72cc33ae0cef6eb16 Mon Sep 17 00:00:00 2001 From: Suzanne Soy Date: Wed, 27 Sep 2023 14:10:23 +0100 Subject: [PATCH 4/6] use separate entry points --- src/main.jsligo | 86 ++++++++++++++++++++++++++++--------------------- 1 file changed, 49 insertions(+), 37 deletions(-) diff --git a/src/main.jsligo b/src/main.jsligo index 023a8a8..2cf95b9 100644 --- a/src/main.jsligo +++ b/src/main.jsligo @@ -23,7 +23,10 @@ export type parameter = export type storage = Storage.t; type result = [list, storage]; -const execute = (outcomeKey: nat, packed: bytes, s: storage) : result => { +@entry +const execute = (outcomeExecuteParam : Outcome.executeParams, s: storage) : result => { + assert_with_error(Tezos.get_amount() == (0 as tez), Errors.notZeroAmount); + const {outcomeKey, packed} = outcomeExecuteParam; const proposal = match(Big_map.find_opt(outcomeKey, s.outcomes)) { when(None): failwith(Errors.outcomeNotFound); when(Some(outcome)): Outcome.getExecutableProposal(outcome) @@ -48,7 +51,10 @@ const execute = (outcomeKey: nat, packed: bytes, s: storage) : result => { }; }; -const propose = (p: Proposal.makeParams, s: storage) : result => +/* +@entry +const propose = (p: Proposal.makeParams, s: storage) : result => { + assert_with_error(Tezos.get_amount() == (0 as tez), Errors.notZeroAmount); match(s.proposal) { when(Some(_proposal)): failwith(Errors.proposalAlreadyExists); when(None): [ @@ -61,17 +67,22 @@ const propose = (p: Proposal.makeParams, s: storage) : result => Proposal.make(p, s.config.startDelay, s.config.votingPeriod), s) ] - }; + } +}; -const cancel = (outcomeKeyOpt: option, s: storage) : result => - [ - list([Token.transfer( - [s.governanceToken, - Tezos.get_self_address(), - s.config.burnAddress, - s.config.depositAmount]) - ]), - match(outcomeKeyOpt) { +@entry +const cancel = (outcomeKeyOpt: option, s: storage) : result => { + assert_with_error(Tezos.get_amount() == (0 as tez), Errors.notZeroAmount); + return [ + list([ + Token.transfer([ + s.governanceToken, + Tezos.get_self_address(), + s.config.burnAddress, + s.config.depositAmount + ]) + ]), + match(outcomeKeyOpt) { when(None): match(s.proposal) { when(None): failwith(Errors.nothingToCancel); @@ -104,9 +115,12 @@ const cancel = (outcomeKeyOpt: option, s: storage) : result => } } } - ]; + ] +}; +@entry const lock = (amount_: nat, s: storage) : result => { + assert_with_error(Tezos.get_amount() == (0 as tez), Errors.notZeroAmount); Proposal._checkNoVoteOngoing(s.proposal); const currentAmount = Vault.getForUser([s.vault, Tezos.get_sender()]); @@ -122,7 +136,9 @@ const lock = (amount_: nat, s: storage) : result => { ]; }; +@entry const release = (amount_: nat, s: storage) : result => { + assert_with_error(Tezos.get_amount() == (0 as tez), Errors.notZeroAmount); Proposal._checkNoVoteOngoing(s.proposal); const currentAmount = Vault.getForUserExn([s.vault, Tezos.get_sender()]); assert_with_error( (currentAmount >= amount_), Errors.notEnoughBalance); @@ -139,18 +155,26 @@ const release = (amount_: nat, s: storage) : result => { ]; }; -const vote = (choice: bool, s: storage) : storage => - match(s.proposal) { - when(None): failwith(Errors.noProposal); - when(Some(proposal)): do { - Proposal._checkIsVotingPeriod(proposal); - const amount_ = Vault.getForUserExn([s.vault, Tezos.get_sender()]); - return Storage.updateVotes(proposal, [choice, amount_], s); +@entry +const vote = (choice: bool, s: storage) : result => { + assert_with_error(Tezos.get_amount() == (0 as tez), Errors.notZeroAmount); + return [ + Constants.noOperation, + match(s.proposal) { + when(None): failwith(Errors.noProposal); + when(Some(proposal)): do { + Proposal._checkIsVotingPeriod(proposal); + const amount_ = Vault.getForUserExn([s.vault, Tezos.get_sender()]); + return Storage.updateVotes(proposal, [choice, amount_], s); + } } - }; + ] +}; -const endVote = (s: storage) : result => - match(s.proposal) { +@entry +const endVote = (s: storage) : result => { + assert_with_error(Tezos.get_amount() == (0 as tez), Errors.notZeroAmount); + return match(s.proposal) { when(None): failwith(Errors.noProposal); when(Some(proposal)): do { Proposal._checkVotingPeriodEnded(proposal); @@ -180,18 +204,6 @@ const endVote = (s: storage) : result => , Storage.addOutcome(outcome, s) ]; } - }; - -@entry -const main = (action: parameter, store: storage) : result => { - assert_with_error(Tezos.get_amount() == (0 as tez), Errors.notZeroAmount); - return match(action) { - when(Propose(proposalMakeParams)): propose(proposalMakeParams, store); - when(Cancel(nOpt)): cancel(nOpt, store); - when(Lock(n)): lock(n, store); - when(Release(n)): release(n, store); - when(Execute(outcomeExecuteParam)): execute(outcomeExecuteParam.outcomeKey, outcomeExecuteParam.packed, store); - when(Vote(voteChoice)): [Constants.noOperation, vote(voteChoice, store)]; - when(EndVote): endVote(store); - }; + } }; +*/ \ No newline at end of file From 0599519b1c4cf5fc8f53338b712465f79802d71a Mon Sep 17 00:00:00 2001 From: Suzanne Soy Date: Wed, 27 Sep 2023 14:24:37 +0100 Subject: [PATCH 5/6] forgot to remove debug comment --- src/main.jsligo | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main.jsligo b/src/main.jsligo index 2cf95b9..f4ecc4e 100644 --- a/src/main.jsligo +++ b/src/main.jsligo @@ -51,7 +51,6 @@ const execute = (outcomeExecuteParam : Outcome.executeParams, s: storage) : resu }; }; -/* @entry const propose = (p: Proposal.makeParams, s: storage) : result => { assert_with_error(Tezos.get_amount() == (0 as tez), Errors.notZeroAmount); @@ -172,7 +171,7 @@ const vote = (choice: bool, s: storage) : result => { }; @entry -const endVote = (s: storage) : result => { +const endVote = (_u : unit, s: storage) : result => { assert_with_error(Tezos.get_amount() == (0 as tez), Errors.notZeroAmount); return match(s.proposal) { when(None): failwith(Errors.noProposal); @@ -205,5 +204,4 @@ const endVote = (s: storage) : result => { ]; } } -}; -*/ \ No newline at end of file +}; \ No newline at end of file From 98d5f6261e4f465d498142cc18c7d447b4801846 Mon Sep 17 00:00:00 2001 From: Suzanne Soy Date: Wed, 27 Sep 2023 14:33:57 +0100 Subject: [PATCH 6/6] Trying to get the tests to pass --- test/helpers/dao.jsligo | 8 ++++---- test/helpers/token.jsligo | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/helpers/dao.jsligo b/test/helpers/dao.jsligo index f04718f..8c659bd 100644 --- a/test/helpers/dao.jsligo +++ b/test/helpers/dao.jsligo @@ -5,8 +5,8 @@ import Lambda = DAO.Lambda; import Outcome = DAO.Outcome; // Some types for readability -type taddr = typed_address; -type contr = contract; +type taddr = typed_address; +type contr = contract; export type originated = { addr: address, taddr: taddr, @@ -54,14 +54,14 @@ const baseStorage : DAO.storage = { // Originate a DAO contract with given init_storage storage const originate = (init_storage: DAO.storage) : originated => { - const [taddr, _x, _y] = Test.originate (DAO.main, init_storage, 0 as tez); + const [taddr, _x, _y] = Test.originate_module (contract_of(DAO), init_storage, 0 as tez); const contr = Test.to_contract (taddr); const addr = Tezos.address (contr); return { addr: addr, taddr: taddr, contr: contr }; }; // Call entry point of DAO contr contract -const call = (p: DAO.parameter, contr: contr): test_exec_result => +const call = (p: parameter_of DAO, contr: contr): test_exec_result => Test.transfer_to_contract (contr, p, 0 as tez); // Entry points call helpers diff --git a/test/helpers/token.jsligo b/test/helpers/token.jsligo index 1430370..2fdbb09 100644 --- a/test/helpers/token.jsligo +++ b/test/helpers/token.jsligo @@ -2,6 +2,7 @@ #import "@ligo/fa/test/fa2/single_asset.test.mligo" "SingleAsset_helper" #import "./assert.jsligo" "Assert" #import "../../src/main.jsligo" "DAO" +#import "../bootstrap/single_asset.mligo" "BootstrapSingleAsset" // Some types for readability type taddr = typed_address; @@ -15,14 +16,13 @@ export type originated = { }; const originate = (tok_amount : nat) : originated => { - const f = "../bootstrap/single_asset.mligo"; const [init_storage, owners, ops] = SingleAsset_helper.get_initial_storage( [tok_amount, tok_amount, tok_amount] ); const v_mich = Test.run(((x:SingleAsset.Storage.t) => x), init_storage); - const [addr, _x, _y] = Test.originate_from_file(f, "main", list(["total_supply"]), v_mich, 0 as tez); - const taddr : taddr = Test.cast_address (addr); + const [taddr, _x, _y] = Test.originate_module(contract_of(BootstrapSingleAsset), v_mich, 0 as tez); const contr = Test.to_contract (taddr); + const addr = Tezos.address (contr); return { addr: addr, taddr: taddr,