Skip to content

Commit

Permalink
Scaffolding accounts contract
Browse files Browse the repository at this point in the history
  • Loading branch information
aaroncox committed Mar 10, 2025
1 parent 4008f07 commit b9248d9
Show file tree
Hide file tree
Showing 16 changed files with 3,065 additions and 0 deletions.
9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ build/api/debug:
build/api/production:
make -C contracts/api build/production

build/accounts:
make -C contracts/accounts build

build/accounts/debug:
make -C contracts/accounts build/debug

build/accounts/production:
make -C contracts/accounts build/production

build/delphihelper:
make -C contracts/delphihelper build

Expand Down
36 changes: 36 additions & 0 deletions contracts/accounts/.clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
BasedOnStyle: LLVM
ColumnLimit: 120
---
Language: Cpp
AccessModifierOffset: -3
AlignConsecutiveAssignments: true
AlignConsecutiveDeclarations: true
AllowShortFunctionsOnASingleLine: All
AlwaysBreakTemplateDeclarations: Yes
BinPackParameters: false
BraceWrapping:
AfterCaseLabel: true
AfterClass: true
AfterControlStatement: Never
AfterEnum: true
AfterFunction: true
AfterNamespace: false
AfterStruct: true
AfterUnion: true
AfterExternBlock: true
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
BreakBeforeBraces: Custom
BreakConstructorInitializers: BeforeComma
BreakInheritanceList: BeforeComma
CompactNamespaces: true
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 1
ContinuationIndentWidth: 3
IndentWidth: 3
PointerAlignment: Left
39 changes: 39 additions & 0 deletions contracts/accounts/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"root": true,
"ignorePatterns": ["node_modules/**"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended",
],
"rules": {
"prettier/prettier": "warn",
"no-console": "warn",
"sort-imports": [
"error",
{
"ignoreCase": true,
"ignoreDeclarationSort": true,
},
],
"@typescript-eslint/no-empty-interface": "off", // TODO: This should be removed before PR #1
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-namespace": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-empty-function": "warn",
"no-inner-declarations": "off",
},
"settings": {
"import/parsers": {
"@typescript-eslint/parser": [".ts", ".tsx"],
},
"import/resolver": {
"typescript-bun": {
"project": true,
"alwaysTryTypes": true,
},
},
},
}
8 changes: 8 additions & 0 deletions contracts/accounts/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
arrowParens: 'always'
bracketSpacing: false
endOfLine: 'lf'
printWidth: 100
semi: false
singleQuote: true
tabWidth: 4
trailingComma: 'es5'
69 changes: 69 additions & 0 deletions contracts/accounts/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/bin/bash
SHELL := /bin/bash
TEST_FILES := $(shell find src -name '*.ts')
BIN := ./node_modules/.bin

SRC_CONTRACT_NAME = accounts
MAINNET_NODE_URL = https://eos.greymass.com
MAINNET_CONTRACT_ACCOUNT = accounts.gm
TESTNET_NODE_URL = https://jungle4.greymass.com
TESTNET_CONTRACT_ACCOUNT = accounts.gm

build: | build/dir
cdt-cpp -abigen -abigen_output=build/${SRC_CONTRACT_NAME}.abi -o build/${SRC_CONTRACT_NAME}.wasm src/${SRC_CONTRACT_NAME}.cpp -R src -I include -I ../${SRC_CONTRACT_NAME}/include -D DEBUG

build/debug: | build/dir
cdt-cpp -abigen -abigen_output=build/${SRC_CONTRACT_NAME}.abi -o build/${SRC_CONTRACT_NAME}.wasm src/${SRC_CONTRACT_NAME}.cpp -R src -I include -I ../${SRC_CONTRACT_NAME}/include -D DEBUG

build/production: | build/dir
cdt-cpp -abigen -abigen_output=build/${SRC_CONTRACT_NAME}.abi -o build/${SRC_CONTRACT_NAME}.wasm src/${SRC_CONTRACT_NAME}.cpp -R src -I include -I ../${SRC_CONTRACT_NAME}/include

build/dir:
mkdir -p build

clean:
rm -rf build

testnet: build/debug
cleos -u $(TESTNET_NODE_URL) set contract $(TESTNET_CONTRACT_ACCOUNT) \
build/ ${SRC_CONTRACT_NAME}.wasm ${SRC_CONTRACT_NAME}.abi

mainnet: build/production
cleos -u $(MAINNET_NODE_URL) set contract $(MAINNET_CONTRACT_ACCOUNT) \
build/ ${SRC_CONTRACT_NAME}.wasm ${SRC_CONTRACT_NAME}.abi

# mainnet: build/production
# cleos -u $(MAINNET_NODE_URL) set contract \
# --dont-broadcast --skip-sign --expiration 259200 --json-file msig.json \
# ${MAINNET_CONTRACT_ACCOUNT} build/ ${SRC_CONTRACT_NAME}.wasm ${SRC_CONTRACT_NAME}.abi
# cleos -u $(MAINNET_NODE_URL) multisig propose_trx ${MAINNET_MSIG_PROPOSAL} ${MAINNET_MSIG_SIGNERS} \
# msig.json ${MAINNET_MSIG_PROPOSER} -p ${MAINNET_MSIG_PERMISSION}

test: build/debug node_modules build/contract.ts
bun test

build/contract.ts:
npx @wharfkit/cli generate --json ./build/${SRC_CONTRACT_NAME}.abi --file ./test/contracts/${SRC_CONTRACT_NAME}.ts ${SRC_CONTRACT_NAME}

codegen/dir:
mkdir -p codegen

codegen/eosio.ts:
npx @wharfkit/cli generate --url https://jungle4.greymass.com --file ./test/contracts/eosio.ts eosio

codegen/eosio.token.ts:
npx @wharfkit/cli generate --url https://jungle4.greymass.com --file ./test/contracts/eosio.token.ts eosio.token

.PHONY: check
check: cppcheck jscheck

.PHONY: cppcheck
cppcheck:
clang-format --dry-run --Werror src/*.cpp include/${SRC_CONTRACT_NAME}/*.hpp

.PHONY: format
format: cppformat jsformat

.PHONY: cppformat
cppformat:
clang-format -i src/*.cpp include/${SRC_CONTRACT_NAME}/*.hpp
Empty file added contracts/accounts/README.md
Empty file.
57 changes: 57 additions & 0 deletions contracts/accounts/include/accounts/accounts.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#pragma once

#include <eosio.msig/eosio.msig.hpp>
#include <eosio.system/eosio.system.hpp>
#include <eosio.token/eosio.token.hpp>
#include <eosio/contract.hpp>
#include <eosio/singleton.hpp>

using namespace eosio;
using namespace std;

namespace accounts {

static constexpr symbol EOS = symbol{"EOS", 4};

class [[eosio::contract("accounts")]] accounts : public contract
{
public:
using contract::contract;

// struct [[eosio::table("tokens")]] token_row
// {
// uint64_t id;
// name contract;
// symbol symbol;
// uint64_t primary_key() const { return id; }
// };
// typedef eosio::multi_index<"tokens"_n, token_row> token_table;

// [[eosio::action]] void addtoken(const token_definition token);

// [[eosio::action, eosio::read_only]] std::vector<asset> getbalances(const name account,
// const std::vector<token_definition> tokens);
// using getbalances_action = action_wrapper<"getbalances"_n, &tokens::getbalances>;

// [[eosio::action, eosio::read_only]] std::vector<token_definition> gettokens();
// using gettokens_action = action_wrapper<"gettokens"_n, &tokens::gettokens>;

#ifdef DEBUG
[[eosio::action]] void wipe();
[[eosio::action]] void reset();
#endif

private:
// void add_token(const token_definition token);
// std::vector<token_definition> get_token_definitions();

#ifdef DEBUG
template <typename T>
void clear_table(T& table, uint64_t rows_to_clear);
void reset_singletons();
void wipe_singletons();
void wipe_tables();
#endif
};

} // namespace accounts
168 changes: 168 additions & 0 deletions contracts/accounts/include/eosio.msig/eosio.msig.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
#pragma once

#include <eosio/binary_extension.hpp>
#include <eosio/eosio.hpp>
#include <eosio/ignore.hpp>
#include <eosio/transaction.hpp>

namespace eosio {
/**
* The `eosio.msig` system contract allows for creation of proposed transactions which require authorization from a list
* of accounts, approval of the proposed transactions by those accounts required to approve it, and finally, it also
* allows the execution of the approved transactions on the blockchain.
*
* In short, the workflow to propose, review, approve and then executed a transaction it can be described by the
* following:
* - first you create a transaction json file,
* - then you submit this proposal to the `eosio.msig` contract, and you also insert the account permissions required to
* approve this proposal into the command that submits the proposal to the blockchain,
* - the proposal then gets stored on the blockchain by the `eosio.msig` contract, and is accessible for review and
* approval to those accounts required to approve it,
* - after each of the appointed accounts required to approve the proposed transactions reviews and approves it, you can
* execute the proposed transaction. The `eosio.msig` contract will execute it automatically, but not before validating
* that the transaction has not expired, it is not cancelled, and it has been signed by all the permissions in the
* initial proposal's required permission list.
*/
class [[eosio::contract("eosio.msig")]] multisig : public contract
{
public:
using contract::contract;

/**
* Propose action, creates a proposal containing one transaction.
* Allows an account `proposer` to make a proposal `proposal_name` which has `requested`
* permission levels expected to approve the proposal, and if approved by all expected
* permission levels then `trx` transaction can we executed by this proposal.
* The `proposer` account is authorized and the `trx` transaction is verified if it was
* authorized by the provided keys and permissions, and if the proposal name doesn’t
* already exist; if all validations pass the `proposal_name` and `trx` trasanction are
* saved in the proposals table and the `requested` permission levels to the
* approvals table (for the `proposer` context). Storage changes are billed to `proposer`.
*
* @param proposer - The account proposing a transaction
* @param proposal_name - The name of the proposal (should be unique for proposer)
* @param requested - Permission levels expected to approve the proposal
* @param trx - Proposed transaction
*/
[[eosio::action]] void
propose(name proposer, name proposal_name, std::vector<permission_level> requested, ignore<transaction> trx);
/**
* Approve action approves an existing proposal. Allows an account, the owner of `level` permission, to approve a
* proposal `proposal_name` proposed by `proposer`. If the proposal's requested approval list contains the `level`
* permission then the `level` permission is moved from internal `requested_approvals` list to
* internal `provided_approvals` list of the proposal, thus persisting the approval for
* the `proposal_name` proposal. Storage changes are billed to `proposer`.
*
* @param proposer - The account proposing a transaction
* @param proposal_name - The name of the proposal (should be unique for proposer)
* @param level - Permission level approving the transaction
* @param proposal_hash - Transaction's checksum
*/
[[eosio::action]] void approve(name proposer,
name proposal_name,
permission_level level,
const eosio::binary_extension<eosio::checksum256>& proposal_hash);
/**
* Unapprove action revokes an existing proposal. This action is the reverse of the `approve` action: if all
* validations pass the `level` permission is erased from internal `provided_approvals` and added to the internal
* `requested_approvals` list, and thus un-approve or revoke the proposal.
*
* @param proposer - The account proposing a transaction
* @param proposal_name - The name of the proposal (should be an existing proposal)
* @param level - Permission level revoking approval for proposal
*/
[[eosio::action]] void unapprove(name proposer, name proposal_name, permission_level level);
/**
* Cancel action cancels an existing proposal.
*
* @param proposer - The account proposing a transaction
* @param proposal_name - The name of the proposal (should be an existing proposal)
* @param canceler - The account cancelling the proposal (only the proposer can cancel an unexpired transaction, and
* the canceler has to be different than the proposer)
*
* Allows the `canceler` account to cancel the `proposal_name` proposal, created by a `proposer`,
* only after time has expired on the proposed transaction. It removes corresponding entries from
* internal proptable and from approval (or old approvals) tables as well.
*/
[[eosio::action]] void cancel(name proposer, name proposal_name, name canceler);
/**
* Exec action allows an `executer` account to execute a proposal.
*
* Preconditions:
* - `executer` has authorization,
* - `proposal_name` is found in the proposals table,
* - all requested approvals are received,
* - proposed transaction is not expired,
* - and approval accounts are not found in invalidations table.
*
* If all preconditions are met the transaction is executed as a deferred transaction,
* and the proposal is erased from the proposals table.
*
* @param proposer - The account proposing a transaction
* @param proposal_name - The name of the proposal (should be an existing proposal)
* @param executer - The account executing the transaction
*/
[[eosio::action]] void exec(name proposer, name proposal_name, name executer);
/**
* Invalidate action allows an `account` to invalidate itself, that is, its name is added to
* the invalidations table and this table will be cross referenced when exec is performed.
*
* @param account - The account invalidating the transaction
*/
[[eosio::action]] void invalidate(name account);

using propose_action = eosio::action_wrapper<"propose"_n, &multisig::propose>;
using approve_action = eosio::action_wrapper<"approve"_n, &multisig::approve>;
using unapprove_action = eosio::action_wrapper<"unapprove"_n, &multisig::unapprove>;
using cancel_action = eosio::action_wrapper<"cancel"_n, &multisig::cancel>;
using exec_action = eosio::action_wrapper<"exec"_n, &multisig::exec>;
using invalidate_action = eosio::action_wrapper<"invalidate"_n, &multisig::invalidate>;

struct [[eosio::table, eosio::contract("eosio.msig")]] proposal
{
name proposal_name;
std::vector<char> packed_transaction;
eosio::binary_extension<std::optional<time_point>> earliest_exec_time;

uint64_t primary_key() const { return proposal_name.value; }
};
typedef eosio::multi_index<"proposal"_n, proposal> proposals;

struct [[eosio::table, eosio::contract("eosio.msig")]] old_approvals_info
{
name proposal_name;
std::vector<permission_level> requested_approvals;
std::vector<permission_level> provided_approvals;
uint64_t primary_key() const { return proposal_name.value; }
};
typedef eosio::multi_index<"approvals"_n, old_approvals_info> old_approvals;
struct approval
{
permission_level level;
time_point time;
};

struct [[eosio::table, eosio::contract("eosio.msig")]] approvals_info
{
uint8_t version = 1;
name proposal_name;
// requested approval doesn't need to contain time, but we want requested approval
// to be of exactly the same size as provided approval, in this case approve/unapprove
// doesn't change serialized data size. So, we use the same type.
std::vector<approval> requested_approvals;
std::vector<approval> provided_approvals;
uint64_t primary_key() const { return proposal_name.value; }
};
typedef eosio::multi_index<"approvals2"_n, approvals_info> approvals;

struct [[eosio::table, eosio::contract("eosio.msig")]] invalidation
{
name account;
time_point last_invalidation_time;

uint64_t primary_key() const { return account.value; }
};

typedef eosio::multi_index<"invals"_n, invalidation> invalidations;
};
} // namespace eosio
Loading

0 comments on commit b9248d9

Please sign in to comment.