Factory Pattern¶
Warning
This is example code for learning purposes. Do not use in production without thorough review and testing.
The factory pattern deploys and registers multiple contract instances. This example shows a factory that registers exchanges and routes trades between them.
Factory Contract¶
1#pragma version >0.3.10
2
3from ethereum.ercs import IERC20
4
5interface Exchange:
6 def token() -> IERC20: view
7 def receive(_from: address, _amt: uint256): nonpayable
8 def transfer(_to: address, _amt: uint256): nonpayable
9
10
11exchange_codehash: public(bytes32)
12# Maps token addresses to exchange addresses
13exchanges: public(HashMap[IERC20, Exchange])
14
15
16@deploy
17def __init__(_exchange_codehash: bytes32):
18 # Register the exchange code hash during deployment of the factory
19 self.exchange_codehash = _exchange_codehash
20
21
22# NOTE: Could implement fancier upgrade logic around self.exchange_codehash
23# For example, allowing the deployer of this contract to change this
24# value allows them to use a new contract if the old one has an issue.
25# This would trigger a cascade effect across all exchanges that would
26# need to be handled appropriately.
27
28
29@external
30def register():
31 # Verify code hash is the exchange's code hash
32 assert msg.sender.codehash == self.exchange_codehash
33 # Save a lookup for the exchange
34 # NOTE: Use exchange's token address because it should be globally unique
35 # NOTE: Should do checks that it hasn't already been set,
36 # which has to be rectified with any upgrade strategy.
37 exchange: Exchange = Exchange(msg.sender)
38 self.exchanges[staticcall exchange.token()] = exchange
39
40
41@external
42def trade(_token1: IERC20, _token2: IERC20, _amt: uint256):
43 # Perform a straight exchange of token1 to token 2 (1:1 price)
44 # NOTE: Any practical implementation would need to solve the price oracle problem
45 extcall self.exchanges[_token1].receive(msg.sender, _amt)
46 extcall self.exchanges[_token2].transfer(msg.sender, _amt)
Exchange Contract¶
1#pragma version >0.3.10
2
3from ethereum.ercs import IERC20
4
5
6interface Factory:
7 def register(): nonpayable
8
9
10token: public(IERC20)
11factory: Factory
12
13
14@deploy
15def __init__(_token: IERC20, _factory: Factory):
16 self.token = _token
17 self.factory = _factory
18
19
20@external
21def initialize():
22 # Anyone can safely call this function because of EXTCODEHASH
23 extcall self.factory.register()
24
25
26# NOTE: This contract restricts trading to only be done by the factory.
27# A practical implementation would probably want counter-pairs
28# and liquidity management features for each exchange pool.
29
30
31@external
32def receive(_from: address, _amt: uint256):
33 assert msg.sender == self.factory.address
34 success: bool = extcall self.token.transferFrom(_from, self, _amt)
35 assert success
36
37
38@external
39def transfer(_to: address, _amt: uint256):
40 assert msg.sender == self.factory.address
41 success: bool = extcall self.token.transfer(_to, _amt)
42 assert success
How the pattern works:
Deploy the Exchange code and record its
codehashDeploy the Factory with the exchange codehash
Deploy Exchange instances (one per token)
Each Exchange calls
factory.register()to register itselfThe Factory verifies the caller’s codehash matches the expected exchange code
Users can now trade between any registered tokens via
factory.trade()
The msg.sender.codehash check ensures only legitimate exchange contracts can register.