forked from runtimeverification/wasm-semantics
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add missing ULM hooks. * Add an Address type * Add a Balance type * Contract storage * Events * Put the contract in a struct * Add contract implementation * Erc20 dispatcher * Erc20 tests
- Loading branch information
1 parent
3ac68c6
commit 39fafa9
Showing
10 changed files
with
915 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
use bytes::Bytes; | ||
use core::cmp::Ordering; | ||
|
||
use crate::decoder::Decodable; | ||
use crate::encoder::{Encodable, EncodingType}; | ||
use crate::unsigned::{U160, U256}; | ||
|
||
#[derive(Debug)] | ||
pub struct Address { | ||
value: U160, | ||
} | ||
|
||
impl Address { | ||
fn new(value: U160) -> Self { | ||
Address { value } | ||
} | ||
|
||
pub fn zero() -> Self { | ||
Address::new(U160::from_u64(0)) | ||
} | ||
|
||
pub fn is_zero(&self) -> bool { | ||
self.value == U160::from_u64(0) | ||
} | ||
|
||
pub fn into_u160(self) -> U160 { | ||
self.value | ||
} | ||
pub fn into_u256(self) -> U256 { | ||
self.value.into() | ||
} | ||
} | ||
|
||
impl From<U160> for Address | ||
{ | ||
fn from(value: U160) -> Self { | ||
Address::new(value) | ||
} | ||
} | ||
impl TryFrom<U256> for Address | ||
{ | ||
type Error = &'static str; | ||
fn try_from(value: U256) -> Result<Self, Self::Error> { | ||
Ok(Address::new(value.try_into()?)) | ||
} | ||
} | ||
impl From<Address> for U160 | ||
{ | ||
fn from(value: Address) -> Self { | ||
value.into_u160() | ||
} | ||
} | ||
impl From<Address> for U256 | ||
{ | ||
fn from(value: Address) -> Self { | ||
value.into_u256() | ||
} | ||
} | ||
|
||
impl Ord for Address { | ||
fn cmp(&self, other: &Self) -> Ordering { | ||
self.value.cmp(&other.value) | ||
} | ||
} | ||
impl PartialOrd for Address { | ||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | ||
Some(self.cmp(other)) | ||
} | ||
} | ||
impl PartialEq for Address { | ||
fn eq(&self, other: &Self) -> bool { | ||
self.cmp(other) == Ordering::Equal | ||
} | ||
} | ||
impl Eq for Address {} | ||
impl Clone for Address { | ||
fn clone(&self) -> Self { | ||
Address { value: self.value.clone() } | ||
} | ||
} | ||
|
||
impl Encodable for Address | ||
{ | ||
fn encode(&self) -> (EncodingType, Bytes) { | ||
self.value.encode() | ||
} | ||
} | ||
impl Decodable for Address | ||
{ | ||
fn encoding_type() -> EncodingType { | ||
U160::encoding_type() | ||
} | ||
fn head_size() -> usize { | ||
U160::head_size() | ||
} | ||
fn decode(bytes: Bytes) -> Self { | ||
Address::new(U160::decode(bytes)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
use bytes::Bytes; | ||
use core::cmp::Ordering; | ||
use core::ops::{Add, Sub}; | ||
|
||
use crate::decoder::Decodable; | ||
use crate::encoder::{Encodable, EncodingType}; | ||
use crate::unsigned::U256; | ||
|
||
#[derive(Debug)] | ||
pub struct Balance { | ||
value: U256 | ||
} | ||
|
||
impl Balance { | ||
pub fn new(value: U256) -> Self { | ||
Balance { value } | ||
} | ||
|
||
pub fn into_u256(self) -> U256 { | ||
self.value | ||
} | ||
|
||
pub fn into_u256_ref(&self) -> &U256 { | ||
&self.value | ||
} | ||
} | ||
|
||
impl TryFrom<U256> for Balance | ||
{ | ||
type Error = &'static str; | ||
fn try_from(value: U256) -> Result<Self, Self::Error> { | ||
Ok(Balance { value }) | ||
} | ||
} | ||
impl From<Balance> for U256 | ||
{ | ||
fn from(value: Balance) -> Self { | ||
value.into_u256() | ||
} | ||
} | ||
|
||
impl Add for &Balance { | ||
type Output = Balance; | ||
fn add(self, other: &Balance) -> Self::Output { | ||
Balance { value: (&self.value) + &other.value } | ||
} | ||
} | ||
impl Add for Balance { | ||
type Output = Balance; | ||
fn add(self, other: Balance) -> Self::Output { | ||
&self + &other | ||
} | ||
} | ||
impl Add<Balance> for &Balance { | ||
type Output = Balance; | ||
fn add(self, other: Balance) -> Self::Output { | ||
self + &other | ||
} | ||
} | ||
impl Add<&Balance> for Balance { | ||
type Output = Balance; | ||
fn add(self, other: &Balance) -> Self::Output { | ||
&self + other | ||
} | ||
} | ||
|
||
impl Sub for &Balance { | ||
type Output = Balance; | ||
fn sub(self, other: &Balance) -> Self::Output { | ||
Balance { value: (&self.value) - &other.value } | ||
} | ||
} | ||
impl Sub for Balance { | ||
type Output = Balance; | ||
fn sub(self, other: Balance) -> Self::Output { | ||
&self - &other | ||
} | ||
} | ||
impl Sub<Balance> for &Balance { | ||
type Output = Balance; | ||
fn sub(self, other: Balance) -> Self::Output { | ||
self - &other | ||
} | ||
} | ||
impl Sub<&Balance> for Balance { | ||
type Output = Balance; | ||
fn sub(self, other: &Balance) -> Self::Output { | ||
&self - other | ||
} | ||
} | ||
|
||
impl Ord for Balance { | ||
fn cmp(&self, other: &Self) -> Ordering { | ||
self.value.cmp(&other.value) | ||
} | ||
} | ||
impl PartialOrd for Balance { | ||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | ||
Some(self.cmp(other)) | ||
} | ||
} | ||
impl PartialEq for Balance { | ||
fn eq(&self, other: &Self) -> bool { | ||
self.cmp(other) == Ordering::Equal | ||
} | ||
} | ||
impl Eq for Balance {} | ||
impl Clone for Balance { | ||
fn clone(&self) -> Self { | ||
Balance { value: self.value.clone() } | ||
} | ||
} | ||
impl Encodable for Balance | ||
{ | ||
fn encode(&self) -> (EncodingType, Bytes) { | ||
self.into_u256_ref().encode() | ||
} | ||
} | ||
impl Decodable for Balance | ||
{ | ||
fn encoding_type() -> EncodingType { | ||
U256::encoding_type() | ||
} | ||
fn head_size() -> usize { | ||
U256::head_size() | ||
} | ||
fn decode(bytes: Bytes) -> Self { | ||
let value = U256::decode(bytes); | ||
Balance { value } | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
use std::cell::RefCell; | ||
use std::rc::Rc; | ||
|
||
use crate::address::Address; | ||
use crate::assertions::fail; | ||
use crate::balance::Balance; | ||
use crate::encoder::Encoder; | ||
use crate::require; | ||
use crate::storage::{SingleChunkStorage, SingleChunkStorageBuilder}; | ||
use crate::ulm::{log3, Ulm}; | ||
use crate::ulm; | ||
|
||
pub struct Erc20 { | ||
api: Rc<RefCell<dyn Ulm>> | ||
} | ||
|
||
impl Erc20 { | ||
pub fn new(api: Rc<RefCell<dyn Ulm>>) -> Self { | ||
Erc20 { api } | ||
} | ||
|
||
// --------------------------- | ||
|
||
fn s_total_supply<'a>(&self) -> SingleChunkStorage<'a, Balance> { | ||
SingleChunkStorageBuilder::new(self.api.clone(), &("total_supply".to_string())).build() | ||
} | ||
|
||
fn s_balances<'a>(&self, address: &Address) -> SingleChunkStorage<'a, Balance> { | ||
let mut builder = SingleChunkStorageBuilder::new(self.api.clone(), &("balances".to_string())); | ||
builder.add_arg(address); | ||
builder.build() | ||
} | ||
|
||
fn s_allowances<'a>(&self, account: &Address, spender: &Address) -> SingleChunkStorage<'a, Balance> { | ||
let mut builder = SingleChunkStorageBuilder::new(self.api.clone(), &("allowances".to_string())); | ||
builder.add_arg(account); | ||
builder.add_arg(spender); | ||
builder.build() | ||
} | ||
|
||
// --------------------------- | ||
|
||
fn transfer_event(&self, from: Address, to: Address, value: &Balance) { | ||
let mut encoder = Encoder::new(); | ||
encoder.add(value); | ||
log3( | ||
&*self.api.borrow(), | ||
"Transfer(address,address,u256)", | ||
&from.into(), &to.into(), | ||
encoder.encode() | ||
) | ||
} | ||
|
||
fn approval_event(&self, owner: Address, spender: Address, value: &Balance) { | ||
let mut encoder = Encoder::new(); | ||
encoder.add(value); | ||
log3( | ||
&*self.api.borrow(), | ||
"Approval(address,address,u256)", | ||
&owner.into(), &spender.into(), | ||
encoder.encode() | ||
) | ||
} | ||
|
||
// --------------------------- | ||
|
||
pub fn init(&self) {} | ||
|
||
pub fn decimals(&self) -> u8 { | ||
18 | ||
} | ||
|
||
pub fn total_supply(&self) -> Balance { | ||
self.s_total_supply().get() | ||
} | ||
|
||
pub fn balance_of(&self, account: &Address) -> Balance { | ||
self.s_balances(account).get() | ||
} | ||
|
||
pub fn transfer(&self, to: &Address, value: &Balance) -> bool { | ||
let owner = ulm::caller(&*self.api.borrow()); | ||
self._transfer(&owner, to, &value); | ||
true | ||
} | ||
|
||
pub fn allowance(&self, owner: &Address, spender: &Address) -> Balance { | ||
self.s_allowances(owner, spender).get() | ||
} | ||
|
||
pub fn approve(&self, spender: &Address, value: &Balance) -> bool { | ||
let owner = ulm::caller(&*self.api.borrow()); | ||
self._approve(&owner, spender, value, true); | ||
true | ||
} | ||
|
||
pub fn transfer_from(&self, from: &Address, to: &Address, value: &Balance) -> bool { | ||
let spender = ulm::caller(&*self.api.borrow()); | ||
self._spend_allowance(from, &spender, value); | ||
self._transfer(from, to, value); | ||
true | ||
} | ||
|
||
fn _transfer(&self, from: &Address, to: &Address, value: &Balance) { | ||
require!(!from.is_zero(), "Invalid sender"); | ||
require!(!to.is_zero(), "Invalid receiver"); | ||
self._update(from, to, value); | ||
self.transfer_event(from.clone(), to.clone(), value); | ||
} | ||
|
||
fn _update(&self, from: &Address, to: &Address, value: &Balance) { | ||
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); | ||
} | ||
} | ||
|
||
pub fn mint(&self, account: &Address, value: &Balance) { | ||
require!(!account.is_zero(), "Zero address"); | ||
self._update(&Address::zero(), account, value); | ||
} | ||
|
||
fn _approve(&self, owner: &Address, spender: &Address, value: &Balance, emit_event: bool) { | ||
require!(!owner.is_zero(), "Invalid approver"); | ||
require!(!spender.is_zero(), "Invalid spender"); | ||
self.s_allowances(owner, spender).set(value.clone()); | ||
if emit_event { | ||
self.approval_event(owner.clone(), spender.clone(), &value); | ||
} | ||
} | ||
|
||
fn _spend_allowance(&self, owner: &Address, spender: &Address, value: &Balance) { | ||
let current_allowance = self.allowance(owner, spender); | ||
require!(value <= ¤t_allowance, "Insuficient allowance"); | ||
self._approve(owner, spender, &(current_allowance - value), false); | ||
} | ||
} | ||
|
||
|
Oops, something went wrong.