Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,33 @@ impl TryFrom<u8> for ProxyType {
}
}

impl TryInto<u8> for ProxyType {
type Error = ();

fn try_into(self) -> Result<u8, Self::Error> {
match self {
Self::Any => Ok(0),
Self::Owner => Ok(1),
Self::NonCritical => Ok(2),
Self::NonTransfer => Ok(3),
Self::Senate => Ok(4),
Self::NonFungible => Ok(5),
Self::Triumvirate => Ok(6),
Self::Governance => Ok(7),
Self::Staking => Ok(8),
Self::Registration => Ok(9),
Self::Transfer => Ok(10),
Self::SmallTransfer => Ok(11),
Self::RootWeights => Ok(12),
Self::ChildKeys => Ok(13),
Self::SudoUncheckedSetCode => Ok(14),
Self::SwapHotkey => Ok(15),
Self::SubnetLeaseBeneficiary => Ok(16),
Self::RootClaim => Err(()),
}
}
}

impl Default for ProxyType {
// allow all Calls; required to be most permissive
fn default() -> Self {
Expand Down
36 changes: 36 additions & 0 deletions evm-tests/src/contracts/proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,5 +144,41 @@ export const IProxyABI = [
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "account",
"type": "bytes32"
}
],
"name": "getProxies",
"outputs": [
{
"components": [
{
"internalType": "bytes32",
"name": "delegate",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "proxy_type",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "delay",
"type": "uint256"
}
],
"internalType": "struct IProxy.ProxyInfo[]",
"name": "",
"type": "tuple[]"
}
],
"stateMutability": "view",
"type": "function"
}
];
63 changes: 55 additions & 8 deletions evm-tests/test/pure-proxy.precompile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@ async function getProxies(api: TypedApi<typeof devnet>, address: string) {

describe("Test pure proxy precompile", () => {
const evmWallet = generateRandomEthersWallet();
// only used for edge case and normal proxy
const evmWallet2 = generateRandomEthersWallet();
const evmWallet3 = generateRandomEthersWallet();
const evmWallet4 = generateRandomEthersWallet();
const receiver = getRandomSubstrateKeypair();

let api: TypedApi<typeof devnet>
Expand All @@ -61,6 +63,7 @@ describe("Test pure proxy precompile", () => {
await forceSetBalanceToEthAddress(api, evmWallet.address)
await forceSetBalanceToEthAddress(api, evmWallet2.address)
await forceSetBalanceToEthAddress(api, evmWallet3.address)
await forceSetBalanceToEthAddress(api, evmWallet4.address)
})

it("Call createPureProxy, then use proxy to call transfer", async () => {
Expand Down Expand Up @@ -130,34 +133,78 @@ describe("Test pure proxy precompile", () => {
const proxies = await api.query.Proxy.Proxies.getValue(convertH160ToSS58(evmWallet2.address))
const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet2)

const proxiesFromContract = await contract.getProxies(convertH160ToPublicKey(evmWallet2.address))
assert.equal(proxiesFromContract.length, proxies[0].length, "proxies length should be equal")

const type = 0;
const delay = 0;

const tx = await contract.addProxy(convertH160ToPublicKey(evmWallet3.address), type, delay)
await tx.wait()


const proxiesAfterAdd = await await api.query.Proxy.Proxies.getValue(convertH160ToSS58(evmWallet2.address))
const proxiesList = proxiesAfterAdd[0].map(proxy => proxy.delegate)

const proxiesFromContractAfterAdd = await contract.getProxies(convertH160ToPublicKey(evmWallet2.address))

const length = proxiesAfterAdd[0].length
assert.equal(length, proxies[0].length + 1, "proxy should be set")
const proxy = proxiesAfterAdd[0][proxiesAfterAdd[0].length - 1]
assert.equal(proxiesFromContractAfterAdd.length, proxiesList.length, "proxy length should be equal")

assert.equal(proxy.delegate, convertH160ToSS58(evmWallet3.address), "proxy should be set")
for (let index = 0; index < proxiesFromContractAfterAdd.length; index++) {
const proxyInfo = proxiesFromContractAfterAdd[index]
let proxySs58 = convertPublicKeyToSs58(proxyInfo[0])
assert.ok(proxiesList.includes(proxySs58), "proxy should be set")
if (index === proxiesFromContractAfterAdd.length - 1) {
assert.equal(Number(proxyInfo[1]), type, "proxy_type should match")
assert.equal(Number(proxyInfo[2]), delay, "delay should match")
}
}

assert.equal(proxiesList.length, proxies[0].length + 1, "proxy should be set")
const proxy = proxiesList[proxiesList.length - 1]

assert.equal(proxy, convertH160ToSS58(evmWallet3.address), "proxy should be set")
const balance = (await api.query.System.Account.getValue(convertPublicKeyToSs58(receiver.publicKey))).data.free

const amount = 1000000000;

const contract2 = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet3)


const callCode = await getTransferCallCode(api, receiver, amount)
const tx2 = await contract2.proxyCall(convertH160ToPublicKey(evmWallet2.address), [type], callCode)
await tx2.wait()

const balanceAfter = (await api.query.System.Account.getValue(convertPublicKeyToSs58(receiver.publicKey))).data.free
assert.equal(balanceAfter, balance + BigInt(amount), "balance should be increased")
})

it("Call addProxy many times, then check getProxies is correct", async () => {
const proxies = await api.query.Proxy.Proxies.getValue(convertH160ToSS58(evmWallet4.address))
const contract = new ethers.Contract(IPROXY_ADDRESS, IProxyABI, evmWallet4)
assert.equal(proxies[0].length, 0, "proxies length should be 0")

const proxiesFromContract = await contract.getProxies(convertH160ToPublicKey(evmWallet4.address))
assert.equal(proxiesFromContract.length, proxies[0].length, "proxies length should be equal")

const type = 1;
const delay = 2;

for (let i = 0; i < 5; i++) {
const evmWallet = generateRandomEthersWallet()
const tx = await contract.addProxy(convertH160ToPublicKey(evmWallet.address), type, delay)
await tx.wait()
}

const proxiesAfterAdd = await await api.query.Proxy.Proxies.getValue(convertH160ToSS58(evmWallet4.address))
const proxiesList = proxiesAfterAdd[0].map(proxy => proxy.delegate)

const proxiesFromContractAfterAdd = await contract.getProxies(convertH160ToPublicKey(evmWallet4.address))

assert.equal(proxiesFromContractAfterAdd.length, proxiesList.length, "proxy length should be equal")

for (let index = 0; index < proxiesFromContractAfterAdd.length; index++) {
const proxyInfo = proxiesFromContractAfterAdd[index]
let proxySs58 = convertPublicKeyToSs58(proxyInfo[0])
assert.ok(proxiesList.includes(proxySs58), "proxy should be set")
assert.equal(Number(proxyInfo[1]), type, "proxy_type should match")
assert.equal(Number(proxyInfo[2]), delay, "delay should match")
}
})
});
34 changes: 33 additions & 1 deletion precompiles/src/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ use frame_system::RawOrigin;
use pallet_evm::{AddressMapping, PrecompileHandle};
use pallet_subtensor_proxy as pallet_proxy;
use precompile_utils::EvmResult;
use sp_core::H256;
use sp_core::{H256, U256};
use sp_runtime::{
codec::DecodeLimit,
traits::{Dispatchable, StaticLookup},
};
use sp_std::boxed::Box;
use sp_std::vec;
use sp_std::vec::Vec;
use subtensor_runtime_common::ProxyType;
pub struct ProxyPrecompile<R>(PhantomData<R>);
Expand Down Expand Up @@ -239,4 +240,35 @@ where

handle.try_dispatch_runtime_call::<R, _>(call, RawOrigin::Signed(account_id))
}

#[precompile::public("getProxies(bytes32)")]
#[precompile::view]
pub fn get_proxies(
_handle: &mut impl PrecompileHandle,
account_id: H256,
) -> EvmResult<Vec<(H256, U256, U256)>> {
let account_id = R::AccountId::from(account_id.0.into());

let proxies = pallet_proxy::pallet::Pallet::<R>::proxies(account_id);
let mut result: Vec<(H256, U256, U256)> = vec![];
for proxy in proxies.0 {
let delegate: [u8; 32] = proxy.delegate.into();
let proxy_type: u8 =
proxy
.proxy_type
.try_into()
.map_err(|_| PrecompileFailure::Error {
exit_status: ExitError::Other("Invalid proxy type".into()),
})?;
let delay: u32 = proxy
.delay
.try_into()
.map_err(|_| PrecompileFailure::Error {
exit_status: ExitError::Other("Invalid delay".into()),
})?;
result.push((delegate.into(), proxy_type.into(), delay.into()));
}

Ok(result)
}
}
160 changes: 160 additions & 0 deletions precompiles/src/solidity/proxy.abi
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
[
{
"type": "function",
"name": "createPureProxy",
"inputs": [
{
"name": "proxy_type",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "delay",
"type": "uint32",
"internalType": "uint32"
},
{
"name": "index",
"type": "uint16",
"internalType": "uint16"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "proxyCall",
"inputs": [
{
"name": "real",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "force_proxy_type",
"type": "uint8[]",
"internalType": "uint8[]"
},
{
"name": "call",
"type": "bytes",
"internalType": "bytes"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "killPureProxy",
"inputs": [
{
"name": "spawner",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "proxy_type",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "index",
"type": "uint16",
"internalType": "uint16"
},
{
"name": "height",
"type": "uint16",
"internalType": "uint16"
},
{
"name": "ext_index",
"type": "uint32",
"internalType": "uint32"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "addProxy",
"inputs": [
{
"name": "delegate",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "proxy_type",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "delay",
"type": "uint32",
"internalType": "uint32"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "removeProxy",
"inputs": [
{
"name": "delegate",
"type": "bytes32",
"internalType": "bytes32"
},
{
"name": "proxy_type",
"type": "uint8",
"internalType": "uint8"
},
{
"name": "delay",
"type": "uint32",
"internalType": "uint32"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "removeProxies",
"inputs": [],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "pokeDeposit",
"inputs": [],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "getProxies",
"inputs": [
{
"name": "account",
"type": "bytes32",
"internalType": "bytes32"
}
],
"outputs": [
{
"name": "",
"type": "bytes32[]",
"internalType": "bytes32[]"
}
],
"stateMutability": "view"
}
]
Loading
Loading