Skip to content

Commit

Permalink
Run the erc20 contract
Browse files Browse the repository at this point in the history
  • Loading branch information
virgil-serbanuta committed Oct 1, 2024
1 parent a0c040b commit 8692ea7
Show file tree
Hide file tree
Showing 10 changed files with 274 additions and 6 deletions.
39 changes: 37 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,16 @@ MX_RUST_TWO_CONTRACTS_TESTING_OUTPUT_DIR ::= .build/mx-rust-two-contracts/output
MX_RUST_TWO_CONTRACTS_TESTING_INPUTS ::= $(wildcard $(MX_RUST_TWO_CONTRACTS_TESTING_INPUT_DIR)/*.run)
MX_RUST_TWO_CONTRACTS_TESTING_OUTPUTS ::= $(patsubst $(MX_RUST_TWO_CONTRACTS_TESTING_INPUT_DIR)/%,$(MX_RUST_TWO_CONTRACTS_TESTING_OUTPUT_DIR)/%.executed.kore,$(MX_RUST_TWO_CONTRACTS_TESTING_INPUTS))

.PHONY: clean build test syntax-test preprocessing-test execution-test mx-test mx-rust-test mx-rust-contract-test mx-rust-two-contracts-test
DEMOS_TESTING_KOMPILED ::= $(MX_RUST_CONTRACT_TESTING_KOMPILED)
DEMOS_TESTING_TIMESTAMP ::= $(DEMOS_TESTING_KOMPILED)/timestamp
DEMOS_TESTING_INPUT_DIR ::= tests/demos
DEMOS_TESTING_OUTPUT_DIR ::= .build/demos/output
DEMOS_TESTING_INPUTS ::= $(wildcard $(DEMOS_TESTING_INPUT_DIR)/*.run)
DEMOS_TESTING_OUTPUTS ::= $(patsubst $(DEMOS_TESTING_INPUT_DIR)/%,$(DEMOS_TESTING_OUTPUT_DIR)/%.executed.kore,$(DEMOS_TESTING_INPUTS))

.PHONY: clean build test syntax-test preprocessing-test execution-test mx-test mx-rust-test mx-rust-contract-test mx-rust-two-contracts-test demos-test

all: build test

clean:
rm -r .build
Expand All @@ -65,7 +74,7 @@ build: $(RUST_PREPROCESSING_TIMESTAMP) \
$(MX_RUST_CONTRACT_TESTING_TIMESTAMP) \
$(MX_RUST_TWO_CONTRACTS_TESTING_TIMESTAMP)

test: build syntax-test preprocessing-test execution-test mx-test mx-rust-test mx-rust-contract-test mx-rust-two-contracts-test
test: build syntax-test preprocessing-test execution-test mx-test mx-rust-test mx-rust-contract-test mx-rust-two-contracts-test demos-test

syntax-test: $(SYNTAX_OUTPUTS)

Expand All @@ -81,6 +90,8 @@ mx-rust-contract-test: $(MX_RUST_CONTRACT_TESTING_OUTPUTS)

mx-rust-two-contracts-test: $(MX_RUST_TWO_CONTRACTS_TESTING_OUTPUTS)

demos-test: $(DEMOS_TESTING_OUTPUTS)

$(RUST_PREPROCESSING_TIMESTAMP): $(RUST_SEMANTICS_FILES)
# Workaround for https://github.com/runtimeverification/k/issues/4141
-rm -r $(RUST_PREPROCESSING_KOMPILED)
Expand Down Expand Up @@ -242,3 +253,27 @@ $(MX_RUST_TWO_CONTRACTS_TESTING_OUTPUT_DIR)/%.run.executed.kore: \
-pARGS2=$(CURDIR)/parsers/args-mx-rust-two-contracts.sh
cat $@.tmp | grep -q "Lbl'-LT-'k'-GT-'{}(dotk{}())"
mv -f $@.tmp $@


# TODO: Add $(shell echo "$<" | sed 's/\.[^.]*.run$$//').rs
# as a dependency
$(DEMOS_TESTING_OUTPUT_DIR)/%.run.executed.kore: \
$(DEMOS_TESTING_INPUT_DIR)/%.run \
$(DEMOS_TESTING_TIMESTAMP) \
$(wildcard parsers/inc-*.sh) \
parsers/args-mx-rust-contract.sh \
parsers/contract-mx-rust-contract.sh \
parsers/test-mx-rust-contract.sh
mkdir -p $(DEMOS_TESTING_OUTPUT_DIR)
krun \
"$(shell echo "$<" | sed 's/\.[^.]*.run$$//').rs" \
--definition $(DEMOS_TESTING_KOMPILED) \
--parser $(CURDIR)/parsers/contract-mx-rust-contract.sh \
--output kore \
--output-file $@.tmp \
-cTEST='$(shell cat $<)' \
-pTEST=$(CURDIR)/parsers/test-mx-rust-contract.sh \
-cARGS='$(shell cat $(patsubst %.run,%.args,$<))' \
-pARGS=$(CURDIR)/parsers/args-mx-rust-contract.sh
cat $@.tmp | grep -q "Lbl'-LT-'k'-GT-'{}(dotk{}())"
mv -f $@.tmp $@
4 changes: 4 additions & 0 deletions mx-rust-semantics/main/glue.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ module MX-RUST-GLUE
syntax MxRustInstruction ::= cloneValue(Expression) [strict]
// TODO: Figure out if we need to do a deeper clone for, e.g., structs
rule cloneValue(ptrValue(_, V:Value)) => mxRustNewValue(V)
rule mxRustGetBuffer(ptrValue(_, i32(BufferId:MInt{32})))
=> mxGetBuffer(MInt2Unsigned(BufferId))
endmodule
```
2 changes: 2 additions & 0 deletions mx-rust-semantics/main/modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ requires "modules/biguint.md"
requires "modules/blockchain.md"
requires "modules/call-value.md"
requires "modules/hooks.md"
requires "modules/managed-buffer.md"
requires "modules/managed-vec.md"
requires "modules/multi-value-encoded.md"
requires "modules/proxy.md"
Expand All @@ -18,6 +19,7 @@ module MX-RUST-MODULES
imports private MX-RUST-MODULES-BLOCKCHAIN
imports private MX-RUST-MODULES-CALL-VALUE
imports private MX-RUST-MODULES-HOOKS
imports private MX-RUST-MODULES-MANAGED-BUFFER
imports private MX-RUST-MODULES-MANAGED-VEC
imports private MX-RUST-MODULES-MULTI-VALUE-ENCODED
imports private MX-RUST-MODULES-PROXY
Expand Down
40 changes: 40 additions & 0 deletions mx-rust-semantics/main/modules/managed-buffer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
```k
module MX-RUST-MODULES-MANAGED-BUFFER
imports private MX-RUST-REPRESENTATION
imports private RUST-SHARED-SYNTAX
// --------------------------------------
syntax MxRustType ::= "managedBufferType" [function, total]
rule managedBufferType
=> rustStructType
( #token("ManagedBuffer", "Identifier"):Identifier
, ( mxRustStructField
( #token("mx_buffer_id", "Identifier"):Identifier
, MxRust#buffer
)
, .MxRustStructFields
)
)
rule mxValueToRust
( #token("ManagedBuffer", "Identifier")
, V:MxValue
)
=> mxToRustTyped(managedBufferType, mxListValue(V , .MxValueList))
rule rustValueToMx
( struct
( #token("ManagedBuffer", "Identifier"):Identifier
, #token("mx_buffer_id", "Identifier"):Identifier |-> VecValueId:Int
.Map
)
)
=> mxRustGetBuffer(ptr(VecValueId))
// --------------------------------------
endmodule
```
4 changes: 0 additions & 4 deletions mx-rust-semantics/main/modules/multi-value-encoded.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,6 @@ module MX-RUST-MODULES-MULTI-VALUE-ENCODED
)
=> mxRustGetBuffer(ptr(VecValueId))
syntax MxRustInstruction ::= mxRustGetBuffer(Expression) [strict]
rule mxRustGetBuffer(ptrValue(_, i32(BufferId:MInt{32})))
=> mxGetBuffer(MInt2Unsigned(BufferId))
// --------------------------------------
endmodule
Expand Down
2 changes: 2 additions & 0 deletions mx-rust-semantics/main/preprocessing/methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,8 @@ module MX-RUST-PREPROCESSING-METHODS
[owise]
rule getMapperValueType(#token("BigUint", "Identifier") #as T:Type)
=> rustType(T)
rule getMapperValueType(#token("ManagedBuffer", "Identifier") #as T:Type)
=> rustType(T)
syntax BlockExpression ::= buildProxyMethodBody
( selfName: SelfSort
Expand Down
1 change: 1 addition & 0 deletions mx-rust-semantics/main/representation.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ module MX-RUST-REPRESENTATION
| mxRustLoadPtr(Int)
| mxRustGetBigIntFromStruct(Value)
| mxRustGetStringFromId(Int)
| mxRustGetBuffer(Expression) [strict]
| mxRustNewStruct(MxRustStructType, CallParamsList)
[strict(2), result(ValueWithPtr)]
| "mxRustCheckMxStatus"
Expand Down
1 change: 1 addition & 0 deletions tests/demos/erc_20_token.1.args
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mxListValue(mxStringValue("MyToken")), mxListValue(mxStringValue("MTKN")), mxIntValue(0)
37 changes: 37 additions & 0 deletions tests/demos/erc_20_token.1.run
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
setCallee("Owner");

push mxListValue();
push mxStringValue("decimals");
push mxIntValue(0);
push mxTransfersValue();
push mxIntValue(0);
push mxStringValue("TestContract");
call 6 MX#managedExecuteOnDestContext;
check_eq mxIntValue(0);

push_return_value;
check_eq mxIntValue(18);

push mxListValue();
push mxStringValue("name");
push mxIntValue(0);
push mxTransfersValue();
push mxIntValue(0);
push mxStringValue("TestContract");
call 6 MX#managedExecuteOnDestContext;
check_eq mxIntValue(0);

push_return_value;
check_eq mxStringValue("MyToken");

push mxListValue();
push mxStringValue("symbol");
push mxIntValue(0);
push mxTransfersValue();
push mxIntValue(0);
push mxStringValue("TestContract");
call 6 MX#managedExecuteOnDestContext;
check_eq mxIntValue(0);

push_return_value;
check_eq mxStringValue("MTKN")
150 changes: 150 additions & 0 deletions tests/demos/erc_20_token.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// This contract is a translation of
// https://github.com/Pi-Squared-Inc/pi2-examples/blob/b63d0a78922874a486be8a0395a627425fb5a052/solidity/src/tokens/SomeToken.sol
//
// The main change is that the contract does not issue specific error objects
// (e.g. ERC20InsufficientBalance), it just calls `require!` with various
// (string) explanations.
//
// Also, the `totalSupply` endpoint is declared implicitely as a view for
// `s_total_supply`.

#![no_std]

#[allow(unused_imports)]
use multiversx_sc::imports::*;

#[multiversx_sc::contract]
pub trait Erc20Token {
#[view(totalSupply)]
#[storage_mapper("total_supply")]
fn s_total_supply(&self) -> SingleValueMapper<BigUint>;

#[view(getName)]
#[storage_mapper("name")]
fn s_name(&self) -> SingleValueMapper<ManagedBuffer>;

#[view(getSymbol)]
#[storage_mapper("symbol")]
fn s_symbol(&self) -> SingleValueMapper<ManagedBuffer>;

#[view(getBalances)]
#[storage_mapper("balances")]
fn s_balances(&self, address: &ManagedAddress) -> SingleValueMapper<BigUint>;

#[view(getAllowances)]
#[storage_mapper("allowances")]
fn s_allowances(&self, account: &ManagedAddress, spender: &ManagedAddress) -> SingleValueMapper<BigUint>;

#[event("Transfer")]
fn transfer_event(&self, #[indexed] from: &ManagedAddress, #[indexed] to: &ManagedAddress, value: &BigUint);
#[event("Approval")]
fn approval_event(&self, #[indexed] owner: &ManagedAddress, #[indexed] spender: &ManagedAddress, value: &BigUint);


#[init]
fn init(&self, name: &ManagedBuffer, symbol: &ManagedBuffer, init_supply: &BigUint) {
self.s_name().set_if_empty(name);
self.s_symbol().set_if_empty(symbol);
self._mint(&self.blockchain().get_caller(), init_supply);
}

#[upgrade]
fn upgrade(&self) {}

#[view(decimals)]
fn decimals(&self) -> u8 {
return 18;
}

// Already declared above
// #[view(totalSupply)]
// fn total_supply(&self) -> BigUint {
// return self.s_total_supply().get();
// }

#[view(name)]
fn name(&self) -> ManagedBuffer {
return self.s_name().get();
}

#[view(symbol)]
fn symbol(&self) -> ManagedBuffer {
return self.s_symbol().get();
}

#[view(balanceOf)]
fn balance_of(&self, account: &ManagedAddress) -> BigUint {
self.s_balances(account).get()
}

#[endpoint(transfer)]
fn transfer(&self, to: &ManagedAddress, value: BigUint) -> bool {
let owner = self.blockchain().get_caller();
self._transfer(&owner, to, &value);
true
}

#[view(allowance)]
fn allowance(&self, owner: &ManagedAddress, spender: &ManagedAddress) -> BigUint {
self.s_allowances(owner, spender).get()
}

#[endpoint(approve)]
fn approve(&self, spender: &ManagedAddress, value: &BigUint) -> bool {
let owner = self.blockchain().get_caller();
self._approve(&owner, spender, value, true);
true
}

#[endpoint(transferFrom)]
fn transfer_from(&self, from: &ManagedAddress, to: &ManagedAddress, value: &BigUint) -> bool {
let spender = self.blockchain().get_caller();
self._spend_allowance(from, &spender, value);
self._transfer(from, to, value);
return true;
}

fn _transfer(&self, from: &ManagedAddress, to: &ManagedAddress, value: &BigUint) {
require!(!from.is_zero(), "Invalid sender");
require!(!to.is_zero(), "Invalid receiver");
self._update(from, to, value);
self.transfer_event(from, to, value);
}

fn _update(&self, from: &ManagedAddress, to: &ManagedAddress, value: &BigUint) {
if from.is_zero() {
self.s_total_supply().set(self.s_total_supply().get() + value);
} else {
let from_balance = self.s_balances(from).get();
require!(value <= &from_balance, "Insufficient balance");
self.s_balances(from).set(self.s_balances(from).get() - value);
};

if to.is_zero() {
self.s_total_supply().set(self.s_total_supply().get() - value);
} else {
self.s_balances(to).set(self.s_balances(to).get() + value);
}
}

fn _mint(&self, account: &ManagedAddress, value: &BigUint) {
require!(!account.is_zero(), "Zero address");
self._update(&ManagedAddress::zero(), account, value);
}

fn _approve(&self, owner: &ManagedAddress, spender: &ManagedAddress, value: &BigUint, emit_event: bool) {
require!(!owner.is_zero(), "Invalid approver");
require!(!spender.is_zero(), "Invalid spender");
self.s_allowances(owner, spender).set(value);
if emit_event {
self.approval_event(owner, spender, value);
}
}

fn _spend_allowance(&self, owner: &ManagedAddress, spender: &ManagedAddress, value: &BigUint) {
let current_allowance = self.allowance(owner, spender);
require!(value <= &current_allowance, "Insuficient allowance");
self._approve(owner, spender, &(current_allowance - value), false);
}

}

0 comments on commit 8692ea7

Please sign in to comment.