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
- Audit contracts before mainnet deployment
- Use OpenZeppelin for standard implementations
- Implement access control for admin functions
- 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