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:

  1. Deploy the Exchange code and record its codehash

  2. Deploy the Factory with the exchange codehash

  3. Deploy Exchange instances (one per token)

  4. Each Exchange calls factory.register() to register itself

  5. The Factory verifies the caller’s codehash matches the expected exchange code

  6. Users can now trade between any registered tokens via factory.trade()

The msg.sender.codehash check ensures only legitimate exchange contracts can register.