Skip to content

Commit

Permalink
Add vesting pallet precompile
Browse files Browse the repository at this point in the history
  • Loading branch information
drewstone committed Jan 18, 2024
1 parent 46e5250 commit 36b3eeb
Show file tree
Hide file tree
Showing 13 changed files with 766 additions and 4 deletions.
31 changes: 31 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ pallet-evm-precompile-preimage = { path = "precompiles/preimage", default-featur
pallet-evm-precompile-registry = { path = "precompiles/precompile-registry", default-features = false }
pallet-evm-precompile-staking = { path = "precompiles/staking", default-features = false }
pallet-evm-precompile-jobs = { path = "precompiles/jobs", default-features = false }
pallet-evm-precompile-vesting = { path = "precompiles/vesting", default-features = false }

# EVM & Ethereum
# (wasm)
Expand Down
5 changes: 2 additions & 3 deletions precompiles/staking/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ impl From<TestAccount> for H160 {
}
}
}
trait H160Conversion {
pub trait H160Conversion {
fn to_h160(&self) -> H160;
}

Expand Down Expand Up @@ -210,7 +210,6 @@ parameter_types! {
pub static SlashDeferDuration: EraIndex = 0;
pub static Period: BlockNumber = 5;
pub static Offset: BlockNumber = 0;

}

impl frame_system::Config for Runtime {
Expand Down Expand Up @@ -285,7 +284,7 @@ parameter_types! {
}

pub type Precompiles<R> =
PrecompileSetBuilder<R, (PrecompileAt<AddressU64<5>, StakingPrecompile<R>>,)>;
PrecompileSetBuilder<R, (PrecompileAt<AddressU64<1>, StakingPrecompile<R>>,)>;

pub type PCall = StakingPrecompileCall<Runtime>;

Expand Down
2 changes: 1 addition & 1 deletion precompiles/utils/src/testing/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ impl<'p, P: PrecompileSet> PrecompilesTester<'p, P> {
}
}

fn execute(&mut self) -> Option<PrecompileResult> {
pub fn execute(&mut self) -> Option<PrecompileResult> {
let handle = &mut self.handle;
handle.subcall_handle = self.subcall_handle.take();
handle.is_static = self.static_call;
Expand Down
63 changes: 63 additions & 0 deletions precompiles/vesting/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
[package]
name = "pallet-evm-precompile-vesting"
version = "0.1.0"
authors = { workspace = true }
edition = "2021"
description = "A Precompile to make pallet-vesting calls encoding accessible to pallet-evm"

[dependencies]
log = { workspace = true }
num_enum = { workspace = true }
rustc-hex = { workspace = true }

# Moonbeam
precompile-utils = { workspace = true }

# Substrate
frame-support = { workspace = true }
frame-system = { workspace = true }
pallet-balances = { workspace = true }
pallet-vesting = { workspace = true }
parity-scale-codec = { workspace = true, features = ["derive"] }
sp-core = { workspace = true }
sp-runtime = { workspace = true }
sp-std = { workspace = true }

# Frontier
evm = { workspace = true, features = ["with-codec"] }
fp-evm = { workspace = true }
pallet-evm = { workspace = true, features = ["forbid-evm-reentrancy"] }

tangle-primitives = { workspace = true }

[dev-dependencies]
derive_more = { workspace = true }
hex-literal = { workspace = true }
serde = { workspace = true }
sha3 = { workspace = true }

# Moonbeam
precompile-utils = { workspace = true, features = ["std", "testing"] }

# Substrate
pallet-balances = { workspace = true, features = ["std"] }
pallet-timestamp = { workspace = true, features = ["std"] }
scale-info = { workspace = true, features = ["derive", "std"] }
sp-io = { workspace = true, features = ["std"] }

[features]
default = ["std"]
std = [
"fp-evm/std",
"frame-support/std",
"frame-system/std",
"pallet-balances/std",
"pallet-evm/std",
"pallet-vesting/std",
"parity-scale-codec/std",
"precompile-utils/std",
"sp-core/std",
"sp-runtime/std",
"sp-std/std",
"tangle-primitives/std",
]
26 changes: 26 additions & 0 deletions precompiles/vesting/Vesting.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.3;

/// @dev The Vesting contract's address.
address constant VESTING_ADDRESS = 0x0000000000000000000000000000000000000801;

/// @dev The Vesting contract's instance.
Vesting constant VESTING_CONTRACT = Vesting(VESTING_ADDRESS);

/// @author The Tangle Team
/// @title Pallet Vesting Interface
/// @title The interface through which solidity contracts will interact with the Vesting pallet
/// @custom:address 0x0000000000000000000000000000000000000801
interface Vesting {
/// @dev Unlock any vested funds of the sender account.
function vest() external returns (uint8);

/// @dev Unlock any vested funds of a `target` account.
/// @param target The address of the account to unlock vested funds for.
function vestOther(bytes32 target) external returns (uint8);

/// @dev Create a vested transfer.
/// @param target The address of the account to transfer funds to.
/// @param index The index of the vesting schedule to transfer.
function vestedTransfer(bytes32 target, uint8 index) external returns (uint8);
}
180 changes: 180 additions & 0 deletions precompiles/vesting/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
// This file is part of Tangle.
// Copyright (C) 2022-2024 Webb Technologies Inc.
//
// This file is part of pallet-evm-precompile-staking package, originally developed by Purestake
// Inc. Pallet-evm-precompile-staking package used in Tangle Network in terms of GPLv3.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! This file contains the implementation of the VestingPrecompile struct which provides an
//! interface between the EVM and the native vesting pallet of the runtime. It allows EVM contracts
//! to call functions of the vesting pallet, in order to allow EVM accounts to claim vested funds.
//!
//! The VestingPrecompile struct implements core methods that correspond to the functions of the
//! vesting pallet. These methods can be called from EVM contracts. They include functions to get
//! the claim vested funds, claim vested funds on behalf of an account, and transfer a vesting
//! schedule.
//!
//! Each method records the gas cost for the operation, performs the requested operation, and
//! returns the result in a format that can be used by the EVM.
//!
//! The VestingPrecompile struct is generic over the Runtime type, which is the type of the runtime
//! that includes the staking pallet. This allows the precompile to work with any runtime that
//! includes the staking pallet and meets the other trait bounds required by the precompile.
#![cfg_attr(not(feature = "std"), no_std)]

#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;

use evm::Runtime;
use fp_evm::PrecompileHandle;
use frame_support::{
dispatch::{GetDispatchInfo, PostDispatchInfo},
ensure,
traits::Currency,
};

use pallet_evm::AddressMapping;
use precompile_utils::prelude::*;
use sp_core::{H160, H256, U256};
use sp_runtime::{
traits::{AccountIdLookup, Dispatchable, StaticLookup},
MultiAddress,
};
use sp_std::{convert::TryInto, marker::PhantomData, vec, vec::Vec};
use tangle_primitives::types::WrappedAccountId32;

type BalanceOf<Runtime> = <<Runtime as pallet_vesting::Config>::Currency as Currency<
<Runtime as frame_system::Config>::AccountId,
>>::Balance;

pub struct VestingPrecompile<Runtime>(PhantomData<Runtime>);

impl<Runtime> VestingPrecompile<Runtime>
where
Runtime: pallet_vesting::Config + pallet_evm::Config,
Runtime::RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo,
<Runtime::RuntimeCall as Dispatchable>::RuntimeOrigin: From<Option<Runtime::AccountId>>,
Runtime::RuntimeCall: From<pallet_vesting::Call<Runtime>>,
BalanceOf<Runtime>: TryFrom<U256> + Into<U256> + solidity::Codec,
Runtime::AccountId: From<WrappedAccountId32>,
{
/// Helper method to parse SS58 address
fn parse_32byte_address(addr: Vec<u8>) -> EvmResult<Runtime::AccountId> {
let addr: Runtime::AccountId = match addr.len() {
// public address of the ss58 account has 32 bytes
32 => {
let mut addr_bytes = [0_u8; 32];
addr_bytes[..].clone_from_slice(&addr[0..32]);

WrappedAccountId32(addr_bytes).into()
},
_ => {
// Return err if account length is wrong
return Err(revert("Error while parsing staker's address"))
},
};

Ok(addr)
}

/// Helper for converting from u8 to RewardDestination
fn convert_to_account_id(payee: H256) -> EvmResult<Runtime::AccountId> {
let payee = match payee {
H256(
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _],
) => {
let ethereum_address = Address(H160::from_slice(&payee.0[10..]));
Runtime::AddressMapping::into_account_id(ethereum_address.0)
},
H256(account) => Self::parse_32byte_address(account.to_vec())?,
};

Ok(payee)
}
}

#[precompile_utils::precompile]
impl<Runtime> VestingPrecompile<Runtime>
where
Runtime: pallet_vesting::Config + pallet_evm::Config,
Runtime::RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo,
<Runtime::RuntimeCall as Dispatchable>::RuntimeOrigin: From<Option<Runtime::AccountId>>,
Runtime::RuntimeCall: From<pallet_vesting::Call<Runtime>>,
BalanceOf<Runtime>: TryFrom<U256> + Into<U256> + solidity::Codec,
Runtime::AccountId: From<WrappedAccountId32>,
{
#[precompile::public("vest()")]
fn vest(handle: &mut impl PrecompileHandle) -> EvmResult<u8> {
handle.record_cost(RuntimeHelper::<Runtime>::db_read_gas_cost())?;

// Make the call to vest the `msg.sender` account.
let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
let call = pallet_vesting::Call::<Runtime>::vest {};

// Dispatch call (if enough gas).
RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call)?;

Ok((0))
}

#[precompile::public("vestOther(bytes32)")]
#[precompile::public("vest_other(bytes32)")]
fn vest_other(handle: &mut impl PrecompileHandle, target: H256) -> EvmResult<u8> {
handle.record_cost(RuntimeHelper::<Runtime>::db_read_gas_cost())?;

// Make the call to vest the `target` account.
let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
let target = Self::convert_to_account_id(target)?;
let tgt = <<Runtime as frame_system::Config>::Lookup as StaticLookup>::unlookup(target);
let call = pallet_vesting::Call::<Runtime>::vest_other { target: tgt };

// Dispatch call (if enough gas).
RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call)?;

Ok((0))
}

#[precompile::public("vestedTransfer(bytes32,uint8)")]
#[precompile::public("vested_transfer(bytes32,uint8)")]
fn vested_transfer(handle: &mut impl PrecompileHandle, target: H256, index: u8) -> EvmResult<u8> {
handle.record_cost(RuntimeHelper::<Runtime>::db_read_gas_cost())?;

// First get the vesting schedule of the `msg.sender`
let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
match pallet_vesting::Vesting::<Runtime>::get(origin.clone()) {
Some(schedules) => {
if index >= schedules.len() as u8 {
return Err(revert("Invalid vesting schedule index"))
}
// Make the call to transfer the vested funds to the `target` account.
let target = Self::convert_to_account_id(target)?;
let tgt =
<<Runtime as frame_system::Config>::Lookup as StaticLookup>::unlookup(target);
let call = pallet_vesting::Call::<Runtime>::vested_transfer {
target: tgt,
schedule: schedules[index as usize],
};

// Dispatch call (if enough gas).
RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call)?;

return Ok((0))
},
None => Err(revert("No vesting schedule found for the sender")),
}
}
}
Loading

0 comments on commit 36b3eeb

Please sign in to comment.