Skip to main content

Smart Contracts on RP1

RP1 supports smart contracts through both EVM (Solidity) and CosmWasm (Rust).

EVM Compatibility

Full Ethereum compatibility via Ethermint integration:

  • Solidity 0.8.x support
  • Standard EVM opcodes
  • Ethers.js / Web3.js compatible
  • MetaMask integration

Deploy with Hardhat

// hardhat.config.js
module.exports = {
networks: {
rp1: {
url: "https://evm.rp.one",
chainId: 1111,
accounts: [process.env.PRIVATE_KEY],
},
rp1Testnet: {
url: "https://evm.testnet.rp.one",
chainId: 1112,
accounts: [process.env.PRIVATE_KEY],
},
},
solidity: "0.8.19",
};
npx hardhat run scripts/deploy.js --network rp1

Example Contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "@rp1/contracts/interfaces/IOracle.sol";
import "@rp1/contracts/interfaces/IPrivacy.sol";

contract PrivateLending {
IOracle public oracle;
IPrivacy public privacy;

mapping(address => uint256) public deposits;
mapping(address => uint256) public borrows;

function deposit() external payable {
deposits[msg.sender] += msg.value;
}

function borrow(uint256 amount) external {
uint256 collateralValue = getCollateralValue(msg.sender);
require(collateralValue >= amount * 150 / 100, "Insufficient collateral");

borrows[msg.sender] += amount;
payable(msg.sender).transfer(amount);
}

function getCollateralValue(address user) public view returns (uint256) {
uint256 price = oracle.getPrice("RP1");
return deposits[user] * price / 1e18;
}
}

CosmWasm Contracts

For Cosmos-native smart contracts:

Setup

# Install cargo-generate
cargo install cargo-generate

# Create new contract
cargo generate --git https://github.com/CosmWasm/cw-template.git --name my-contract
cd my-contract

Example Contract

use cosmwasm_std::{
entry_point, to_binary, Binary, Deps, DepsMut, Env,
MessageInfo, Response, StdResult,
};
use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg};
use crate::state::{State, STATE};

#[entry_point]
pub fn instantiate(
deps: DepsMut,
_env: Env,
info: MessageInfo,
msg: InstantiateMsg,
) -> StdResult<Response> {
let state = State {
owner: info.sender.clone(),
count: msg.count,
};
STATE.save(deps.storage, &state)?;
Ok(Response::new()
.add_attribute("method", "instantiate")
.add_attribute("owner", info.sender))
}

#[entry_point]
pub fn execute(
deps: DepsMut,
_env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> StdResult<Response> {
match msg {
ExecuteMsg::Increment {} => execute_increment(deps),
ExecuteMsg::Reset { count } => execute_reset(deps, info, count),
}
}

#[entry_point]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
match msg {
QueryMsg::GetCount {} => to_binary(&query_count(deps)?),
}
}

Deploy CosmWasm

# Build contract
cargo build --release --target wasm32-unknown-unknown

# Optimize
docker run --rm -v "$(pwd)":/code \
cosmwasm/rust-optimizer:0.12.13

# Store on chain
rp1d tx wasm store artifacts/my_contract.wasm \
--from my-wallet \
--gas auto

# Instantiate
rp1d tx wasm instantiate <CODE_ID> '{"count": 0}' \
--label "my-counter" \
--admin $(rp1d keys show my-wallet -a) \
--from my-wallet

RP1 Contract Libraries

Oracle Integration

import "@rp1/contracts/interfaces/IOracle.sol";

contract MyDeFi {
IOracle constant ORACLE = IOracle(0x...);

function getPrice(string memory symbol) public view returns (uint256) {
return ORACLE.getPrice(symbol);
}
}

Privacy Integration

import "@rp1/contracts/interfaces/IPrivacy.sol";

contract PrivateVault {
IPrivacy constant PRIVACY = IPrivacy(0x...);

function shieldedDeposit(uint256 amount) external {
// Shield tokens for privacy
PRIVACY.shield(msg.sender, amount);
}
}

Lending Integration

import "@rp1/contracts/interfaces/ILending.sol";

contract LeveragedPosition {
ILending constant LENDING = ILending(0x...);

function openLeveragedPosition() external payable {
// Supply collateral
LENDING.supply{value: msg.value}();

// Borrow against it
uint256 borrowAmount = msg.value * 70 / 100;
LENDING.borrow(borrowAmount);
}
}

Best Practices

Security

  1. Audit contracts before mainnet deployment
  2. Use OpenZeppelin for standard implementations
  3. Implement access control for admin functions
  4. Test thoroughly on testnet first

Gas Optimization

// Use calldata for read-only arrays
function processOrders(Order[] calldata orders) external {
// ...
}

// Pack storage variables
struct Position {
uint128 amount; // 16 bytes
uint64 timestamp; // 8 bytes
uint64 leverage; // 8 bytes
} // Total: 32 bytes (1 slot)

// Use unchecked for safe arithmetic
unchecked {
i++; // When overflow is impossible
}

Upgradeability

import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

contract MyUpgradeableContract is Initializable, UUPSUpgradeable {
function initialize() public initializer {
__UUPSUpgradeable_init();
}

function _authorizeUpgrade(address) internal override onlyOwner {}
}

Deployment Checklist

  • Contract compiles without warnings
  • Unit tests pass (100% coverage recommended)
  • Integration tests on testnet
  • Security audit completed
  • Gas costs analyzed
  • Admin keys secured (multisig recommended)
  • Upgrade path documented
  • Monitoring/alerts configured

Next Steps